Giter Site home page Giter Site logo

parental's Introduction

Parental - Use single table inheritance in your Laravel App

Parental

Parental is a Laravel package that brings STI (Single Table Inheritance) capabilities to Eloquent.

What is single table inheritance (STI)?

It's a fancy name for a simple concept: Extending a model (usually to add specific behavior), but referencing the same table.

Installation

composer require tightenco/parental

Simple Usage

namespace App\Models;

use Illuminate\Database\Eloquent\Model;
use Parental\HasChildren;

// The "parent"
class User extends Model
{
    use HasChildren;
    //
}
namespace App\Models;

use Parental\HasParent;

// The "child"
class Admin extends User
{
    use HasParent;

    public function impersonate($user) {
        //...
    }
}
use App\Models\Admin;

// Returns "Admin" model, but reference "users" table:
$admin = Admin::first();

// Can now access behavior exclusive to "Admin"s
$admin->impersonate($user);

What problem did we just solve?

Without Parental, calling Admin::first() would throw an error because Laravel would be looking for an admins table. Laravel generates expected table names, as well as foreign keys and pivot table names, using the model's class name. By adding the HasParent trait to the Admin model, Laravel will now reference the parent model's class name users.

Accessing Child Models from Parents

// First, we need to create a `type` column on the `users` table
Schema::table('users', function ($table) {
    $table->string('type')->nullable();
});
namespace App\Models;

use Illuminate\Database\Eloquent\Model;
use Parental\HasChildren;

// The "parent"
class User extends Model
{
    use HasChildren;

    protected $fillable = ['type'];
}
namespace App\Models;

use Parental\HasParent;

// A "child"
class Admin extends User
{
    use HasParent;
}
namespace App\Models;

use Parental\HasParent;

// Another "child"
class Guest extends User
{
    use HasParent;
}
use App\Models\Admin;
use App\Models\Guest;
use App\Models\User;

// Adds row to "users" table with "type" column set to: "App/Admin"
Admin::create(...);

// Adds row to "users" table with "type" column set to: "App/Guest"
Guest::create(...);

// Returns 2 model instances: Admin, and Guest
User::all();

What problem did we just solve?

Before, if we ran: User::first() we would only get back User models. By adding the HasChildren trait and a type column to the users table, running User::first() will return an instance of the child model (Admin or Guest in this case).

Type Aliases

If you don't want to store raw class names in the type column, you can override them using the $childTypes property.

namespace App\Models;

use Illuminate\Database\Eloquent\Model;
use Parental\HasChildren;

class User extends Model
{
    use HasChildren;

    protected $fillable = ['type'];

    protected $childTypes = [
        'admin' => Admin::class,
        'guest' => Guest::class,
    ];
}

Now, running Admin::create() will set the type column in the users table to admin instead of App\Models\Admin.

This feature is useful if you are working with an existing type column, or if you want to decouple application details from your database.

Custom Type Column Name

You can override the default type column by setting the $childColumn property on the parent model.

namespace App\Models;

use Illuminate\Database\Eloquent\Model;
use Parental\HasChildren;

class User extends Model
{
    use HasChildren;

    protected $fillable = ['parental_type'];

    protected $childColumn = 'parental_type';
}

Laravel Nova Support

If you want to use share parent Nova resources with child models, you may register the following provider at the end of the boot method of your NovaServiceProvider:

class NovaServiceProvider extends NovaApplicationServiceProvider
{
    public function boot() {
        parent::boot();
        // ...
        $this->app->register(\Parental\Providers\NovaResourceProvider::class);
    }
}

Thanks to @sschoger for the sick logo design, and @DanielCoulbourne for helping brainstorm the idea on Twenty Percent Time.

parental's People

Contributors

austenc avatar bertvanhoekelen avatar calebporzio avatar dallincoons avatar deadpunk avatar driftingly avatar gcavanunez avatar gitkv avatar josecanhelp avatar juststeveking avatar kbond avatar laravel-shift avatar lenrick avatar likeadeckofcards avatar mateusjunges avatar mattstauffer avatar michelbrito avatar nelson6e65 avatar nguyentranchung avatar nickels avatar shuvroroy avatar smoggert avatar tikkibar avatar tobyzerner avatar tonysm avatar tsterker avatar tumainimosha avatar wilfredchen avatar zecipriano avatar ziming avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

parental's Issues

Nesting support & Automatic join?

So, just curious here, what happens if we nest?
E.g. Animal > Mammal > Dog

Not really convinced there's a practical use case for this, more just curious.

Also, Would it be possible to add a new "HasOwnData" or similar trait, if included then the system should automatically join onto a table (ideally we could reuse $table, but I get it if we can't, so maybe $childTable) and include it's fields as well?

This would allow e.g. Admins to have their own data.

Eager loading issue :(

I have a parent model called Category, and two category types, Link & Dynamic. Only one of them has the relationship criteriable()

When I try to load like,

$categories->load('criteria');

I get the error, Call to undefined relationship [criteria] on model [App\Models\Category\Category]' Is eager loading that relationship possible?

Category

class Category extends Model
{
    use Filtered, HasChildren;

    /**
     * @var array
     */
    protected $childTypes = [
        'dynamic' => Dynamic::class,
        'link' => Link::class,
        'regular' => Regular::class,
    ];

    /**
     * @var array
     */
    protected $fillable = [
        'type',
        'name',
    ];

Dynamic

class Dynamic extends Category implements CategoryType, Criteriable
{
    use HasParent;

    /**
     * Criteria
     * @return MorphOne
     */
    public function criteria()
    {
        return $this->morphOne(Criteria::class, 'criteriable');
    }

Link

class Link extends Category implements CategoryType
{
    use HasParent;

    /*
     * No relationships
     */

Q: Nova child models?

Hey everyone

I read some tickets here and was able to get Parental to work Nova, at least for my parent resource 'Asset'. I have several sub-assets and would like to have separate administration entries for each one, e.g. A extends Asset, B extends Asset etc. and according resources.

But no matter what I do, these always fail to load in Nova. They are show in the main menu, loading them up fails with a JavaScript error in Nova Cannot read property 'length' of undefined.

The parent works, but I would rather have it separated, especially as some children have additional fields. Is that possible?

Thanks a lot for any help in advance.

Call functions from child classes working with authenticated users

This package works grate in tearms of incapsulation, I moved all logic into child classes exteding User model.
However, it not possible anymore to work with authenticated users.
I cannot call Auth::user()->profile() anymore, because this relation was extracted into subclass.
It would be great to implement something like Auth::admin(), Auth::manager() in this package to work with authenticated users.
Maybe It wold be nice to add some tutorial to readme explaining how to do it.

Use parent's fillables in children models

When you create a parent model that has some fillable attributes and later have a child model that extends that, the child model needs to duplicate its parent's fillable attributes. In the following example, a User has a name and email, and a spy is a type of user that also has a secret_spy_id. Currently, if I do Spy::create(['name' => 'MikeSpy' ...]), I will get an exception about the name and email NOT NULL constraints being violated. This is, because Spy does not recocnize name or email as fillable attributes, and thus does not populate the fields or reflect that to the database. The following are the example classes:

class User extends Model {
    use HasChildren;

    protected $fillable = [
        'name',
        'email',
    ];
}
class Spy extends User {
    use HasParent;

    protected $fillable = [
        'secret_spy_id',
    ];
}

The above won't work unless we do the following:

class Spy extends User {
    use HasParent;

    protected $fillable = [
        'name',
        'email',
        'secret_spy_id',
    ];
}

I imagine fixing this by adding a method in HasParent that overrides Model::getFillable() and returns a merge of $this->fillable and the parent's getFillable() method. This method would look like this:

public function getFillable()
{
    $parentFillable = (new ReflectionClass($this))->getParentClass()->newInstance()->getFillable();
    return array_merge($this->fillable, $parentFillable);
}

Incompatible with Laravel 7.1

First of all, thanks for this perfect package!

Unfortunately it seems Parental is incompatible with Laravel 7.1. Any idea how to solve this is welcome! Thanks!

Your requirements could not be resolved to an installable set of packages.

  Problem 1
    - Conclusion: don't install calebporzio/parental v0.9.1
    - Conclusion: remove calebporzio/parental v0.9.0
    - Conclusion: don't install laravel/framework v7.1.0
    - Conclusion: don't install laravel/framework v7.0.8
    - Conclusion: don't install laravel/framework v7.0.7
    - Conclusion: don't install laravel/framework v7.0.6
    - Conclusion: don't install laravel/framework v7.0.5
    - Conclusion: don't install laravel/framework v7.0.4
    - Conclusion: don't install laravel/framework v7.0.3
    - Conclusion: don't install laravel/framework v7.0.2
    - Conclusion: don't install laravel/framework v7.0.1
    - Installation request for calebporzio/parental ^0.9 -> satisfiable by calebporzio/parental[v0.9.0, v0.9.1].
    - Conclusion: don't install laravel/framework v7.0.0
    - calebporzio/parental v0.9.0 requires illuminate/database ~5.6.0|~5.7.0|~5.8.0|^6.0.0 -> satisfiable by laravel/framework[v6.17.0, 5.8.x-dev, 6.x-dev], illuminate/database[5.6.x-dev, 5.7.17, 5.7.18, 5.7.19, 5.7.x-dev, 5.8.x-dev, 6.x-dev, v5.6.0, v5.6.1, v5.6.10, v5.6.11, v5.6.12, v5.6.13, v5.6.14, v5.6.15, v5.6.16, v5.6.17, v5.6.19, v5.6.2, v5.6.20, v5.6.21, v5.6.22, v5.6.23, v5.6.24, v5.6.25, v5.6.26, v5.6.27, v5.6.28, v5.6.29, v5.6.3, v5.6.30, v5.6.31, v5.6.32, v5.6.33, v5.6.34, v5.6.35, v5.6.36, v5.6.37, v5.6.38, v5.6.39, v5.6.4, v5.6.5, v5.6.6, v5.6.7, v5.6.8, v5.6.9, v5.7.0, v5.7.1, v5.7.10, v5.7.11, v5.7.15, v5.7.2, v5.7.20, v5.7.21, v5.7.22, v5.7.23, v5.7.26, v5.7.27, v5.7.28, v5.7.3, v5.7.4, v5.7.5, v5.7.6, v5.7.7, v5.7.8, v5.7.9, v5.8.0, v5.8.11, v5.8.12, v5.8.14, v5.8.15, v5.8.17, v5.8.18, v5.8.19, v5.8.2, v5.8.20, v5.8.22, v5.8.24, v5.8.27, v5.8.28, v5.8.29, v5.8.3, v5.8.30, v5.8.31, v5.8.32, v5.8.33, v5.8.34, v5.8.35, v5.8.36, v5.8.4, v5.8.8, v5.8.9, v6.0.0, v6.0.1, v6.0.2, v6.0.3, v6.0.4, v6.1.0, v6.10.0, v6.11.0, v6.12.0, v6.13.0, v6.13.1, v6.14.0, v6.15.0, v6.15.1, v6.16.0, v6.17.0, v6.17.1, v6.18.0, v6.18.1, v6.2.0, v6.3.0, v6.4.1, v6.5.0, v6.5.1, v6.5.2, v6.6.0, v6.6.1, v6.6.2, v6.7.0, v6.8.0].
    - Can only install one of: laravel/framework[7.x-dev, 5.8.x-dev].
    - Can only install one of: laravel/framework[7.x-dev, 6.x-dev].
    - Can only install one of: laravel/framework[7.x-dev, v6.17.0].
    - don't install illuminate/database 5.6.x-dev|don't install laravel/framework 7.x-dev
    - don't install illuminate/database 5.7.17|don't install laravel/framework 7.x-dev
    - don't install illuminate/database 5.7.18|don't install laravel/framework 7.x-dev
    - don't install illuminate/database 5.7.19|don't install laravel/framework 7.x-dev
    - don't install illuminate/database 5.7.x-dev|don't install laravel/framework 7.x-dev
    - don't install illuminate/database 5.8.x-dev|don't install laravel/framework 7.x-dev
    - don't install illuminate/database 6.x-dev|don't install laravel/framework 7.x-dev
    - don't install illuminate/database v5.6.0|don't install laravel/framework 7.x-dev
    - don't install illuminate/database v5.6.1|don't install laravel/framework 7.x-dev
    - don't install illuminate/database v5.6.10|don't install laravel/framework 7.x-dev
    - don't install illuminate/database v5.6.11|don't install laravel/framework 7.x-dev
    - don't install illuminate/database v5.6.12|don't install laravel/framework 7.x-dev
    - don't install illuminate/database v5.6.13|don't install laravel/framework 7.x-dev
    - don't install illuminate/database v5.6.14|don't install laravel/framework 7.x-dev
    - don't install illuminate/database v5.6.15|don't install laravel/framework 7.x-dev
    - don't install illuminate/database v5.6.16|don't install laravel/framework 7.x-dev
    - don't install illuminate/database v5.6.17|don't install laravel/framework 7.x-dev
    - don't install illuminate/database v5.6.19|don't install laravel/framework 7.x-dev
    - don't install illuminate/database v5.6.2|don't install laravel/framework 7.x-dev
    - don't install illuminate/database v5.6.20|don't install laravel/framework 7.x-dev
    - don't install illuminate/database v5.6.21|don't install laravel/framework 7.x-dev
    - don't install illuminate/database v5.6.22|don't install laravel/framework 7.x-dev
    - don't install illuminate/database v5.6.23|don't install laravel/framework 7.x-dev
    - don't install illuminate/database v5.6.24|don't install laravel/framework 7.x-dev
    - don't install illuminate/database v5.6.25|don't install laravel/framework 7.x-dev
    - don't install illuminate/database v5.6.26|don't install laravel/framework 7.x-dev
    - don't install illuminate/database v5.6.27|don't install laravel/framework 7.x-dev
    - don't install illuminate/database v5.6.28|don't install laravel/framework 7.x-dev
    - don't install illuminate/database v5.6.29|don't install laravel/framework 7.x-dev
    - don't install illuminate/database v5.6.3|don't install laravel/framework 7.x-dev
    - don't install illuminate/database v5.6.30|don't install laravel/framework 7.x-dev
    - don't install illuminate/database v5.6.31|don't install laravel/framework 7.x-dev
    - don't install illuminate/database v5.6.32|don't install laravel/framework 7.x-dev
    - don't install illuminate/database v5.6.33|don't install laravel/framework 7.x-dev
    - don't install illuminate/database v5.6.34|don't install laravel/framework 7.x-dev
    - don't install illuminate/database v5.6.35|don't install laravel/framework 7.x-dev
    - don't install illuminate/database v5.6.36|don't install laravel/framework 7.x-dev
    - don't install illuminate/database v5.6.37|don't install laravel/framework 7.x-dev
    - don't install illuminate/database v5.6.38|don't install laravel/framework 7.x-dev
    - don't install illuminate/database v5.6.39|don't install laravel/framework 7.x-dev
    - don't install illuminate/database v5.6.4|don't install laravel/framework 7.x-dev
    - don't install illuminate/database v5.6.5|don't install laravel/framework 7.x-dev
    - don't install illuminate/database v5.6.6|don't install laravel/framework 7.x-dev
    - don't install illuminate/database v5.6.7|don't install laravel/framework 7.x-dev
    - don't install illuminate/database v5.6.8|don't install laravel/framework 7.x-dev
    - don't install illuminate/database v5.6.9|don't install laravel/framework 7.x-dev
    - don't install illuminate/database v5.7.0|don't install laravel/framework 7.x-dev
    - don't install illuminate/database v5.7.1|don't install laravel/framework 7.x-dev
    - don't install illuminate/database v5.7.10|don't install laravel/framework 7.x-dev
    - don't install illuminate/database v5.7.11|don't install laravel/framework 7.x-dev
    - don't install illuminate/database v5.7.15|don't install laravel/framework 7.x-dev
    - don't install illuminate/database v5.7.2|don't install laravel/framework 7.x-dev
    - don't install illuminate/database v5.7.20|don't install laravel/framework 7.x-dev
    - don't install illuminate/database v5.7.21|don't install laravel/framework 7.x-dev
    - don't install illuminate/database v5.7.22|don't install laravel/framework 7.x-dev
    - don't install illuminate/database v5.7.23|don't install laravel/framework 7.x-dev
    - don't install illuminate/database v5.7.26|don't install laravel/framework 7.x-dev
    - don't install illuminate/database v5.7.27|don't install laravel/framework 7.x-dev
    - don't install illuminate/database v5.7.28|don't install laravel/framework 7.x-dev
    - don't install illuminate/database v5.7.3|don't install laravel/framework 7.x-dev
    - don't install illuminate/database v5.7.4|don't install laravel/framework 7.x-dev
    - don't install illuminate/database v5.7.5|don't install laravel/framework 7.x-dev
    - don't install illuminate/database v5.7.6|don't install laravel/framework 7.x-dev
    - don't install illuminate/database v5.7.7|don't install laravel/framework 7.x-dev
    - don't install illuminate/database v5.7.8|don't install laravel/framework 7.x-dev
    - don't install illuminate/database v5.7.9|don't install laravel/framework 7.x-dev
    - don't install illuminate/database v5.8.0|don't install laravel/framework 7.x-dev
    - don't install illuminate/database v5.8.11|don't install laravel/framework 7.x-dev
    - don't install illuminate/database v5.8.12|don't install laravel/framework 7.x-dev
    - don't install illuminate/database v5.8.14|don't install laravel/framework 7.x-dev
    - don't install illuminate/database v5.8.15|don't install laravel/framework 7.x-dev
    - don't install illuminate/database v5.8.17|don't install laravel/framework 7.x-dev
    - don't install illuminate/database v5.8.18|don't install laravel/framework 7.x-dev
    - don't install illuminate/database v5.8.19|don't install laravel/framework 7.x-dev
    - don't install illuminate/database v5.8.2|don't install laravel/framework 7.x-dev
    - don't install illuminate/database v5.8.20|don't install laravel/framework 7.x-dev
    - don't install illuminate/database v5.8.22|don't install laravel/framework 7.x-dev
    - don't install illuminate/database v5.8.24|don't install laravel/framework 7.x-dev
    - don't install illuminate/database v5.8.27|don't install laravel/framework 7.x-dev
    - don't install illuminate/database v5.8.28|don't install laravel/framework 7.x-dev
    - don't install illuminate/database v5.8.29|don't install laravel/framework 7.x-dev
    - don't install illuminate/database v5.8.3|don't install laravel/framework 7.x-dev
    - don't install illuminate/database v5.8.30|don't install laravel/framework 7.x-dev
    - don't install illuminate/database v5.8.31|don't install laravel/framework 7.x-dev
    - don't install illuminate/database v5.8.32|don't install laravel/framework 7.x-dev
    - don't install illuminate/database v5.8.33|don't install laravel/framework 7.x-dev
    - don't install illuminate/database v5.8.34|don't install laravel/framework 7.x-dev
    - don't install illuminate/database v5.8.35|don't install laravel/framework 7.x-dev
    - don't install illuminate/database v5.8.36|don't install laravel/framework 7.x-dev
    - don't install illuminate/database v5.8.4|don't install laravel/framework 7.x-dev
    - don't install illuminate/database v5.8.8|don't install laravel/framework 7.x-dev
    - don't install illuminate/database v5.8.9|don't install laravel/framework 7.x-dev
    - don't install illuminate/database v6.0.0|don't install laravel/framework 7.x-dev
    - don't install illuminate/database v6.0.1|don't install laravel/framework 7.x-dev
    - don't install illuminate/database v6.0.2|don't install laravel/framework 7.x-dev
    - don't install illuminate/database v6.0.3|don't install laravel/framework 7.x-dev
    - don't install illuminate/database v6.0.4|don't install laravel/framework 7.x-dev
    - don't install illuminate/database v6.1.0|don't install laravel/framework 7.x-dev
    - don't install illuminate/database v6.10.0|don't install laravel/framework 7.x-dev
    - don't install illuminate/database v6.11.0|don't install laravel/framework 7.x-dev
    - don't install illuminate/database v6.12.0|don't install laravel/framework 7.x-dev
    - don't install illuminate/database v6.13.0|don't install laravel/framework 7.x-dev
    - don't install illuminate/database v6.13.1|don't install laravel/framework 7.x-dev
    - don't install illuminate/database v6.14.0|don't install laravel/framework 7.x-dev
    - don't install illuminate/database v6.15.0|don't install laravel/framework 7.x-dev
    - don't install illuminate/database v6.15.1|don't install laravel/framework 7.x-dev
    - don't install illuminate/database v6.16.0|don't install laravel/framework 7.x-dev
    - don't install illuminate/database v6.17.0|don't install laravel/framework 7.x-dev
    - don't install illuminate/database v6.17.1|don't install laravel/framework 7.x-dev
    - don't install illuminate/database v6.18.0|don't install laravel/framework 7.x-dev
    - don't install illuminate/database v6.18.1|don't install laravel/framework 7.x-dev
    - don't install illuminate/database v6.2.0|don't install laravel/framework 7.x-dev
    - don't install illuminate/database v6.3.0|don't install laravel/framework 7.x-dev
    - don't install illuminate/database v6.4.1|don't install laravel/framework 7.x-dev
    - don't install illuminate/database v6.5.0|don't install laravel/framework 7.x-dev
    - don't install illuminate/database v6.5.1|don't install laravel/framework 7.x-dev
    - don't install illuminate/database v6.5.2|don't install laravel/framework 7.x-dev
    - don't install illuminate/database v6.6.0|don't install laravel/framework 7.x-dev
    - don't install illuminate/database v6.6.1|don't install laravel/framework 7.x-dev
    - don't install illuminate/database v6.6.2|don't install laravel/framework 7.x-dev
    - don't install illuminate/database v6.7.0|don't install laravel/framework 7.x-dev
    - don't install illuminate/database v6.8.0|don't install laravel/framework 7.x-dev
    - Installation request for laravel/framework ^7.0 -> satisfiable by laravel/framework[7.x-dev, v7.0.0, v7.0.1, v7.0.2, v7.0.3, v7.0.4, v7.0.5, v7.0.6, v7.0.7, v7.0.8, v7.1.0]

Deleting by id from child deletes all records associated with that id

I have been trying to figure why deleting child records doesn't just delete that specific child but actually any record associated with the current id.

Presumably deleting should work the same as creating?
Parent is Content, Children are Review, Posts, Pages
example:

Review::find(1)->delete();

Review::destroy(1); //also deletes all child records

This deletes all Review, Posts and Pages with the id of 1 instead of adding the type field check.

I am a little surprised no one else is reporting this issue so I am wondering what I am doing wrong?

Can I check the type in another table?

Hey,

Instead of using the $childColumn, can i implement an own way of checking the type?
I'm using spatie/laravel-permission and it adds the role in another table.

Would appreciate if you can clarify on this.

Warm regards,
Akmal Hazim

How to properly use the childTypes variable for authenticated routes

Love the idea of the package but I'm running into some issues with properly defining the $childTypes. I've added a char value type for my user where each char refers to a different child so I setup my $childtypes like below:

protected array $childTypes = [
        'A' => Admin::class,
        'U' => SearchUser::class,
        'C' => CompanyUser::class,
];

But now when I try to visit a page where the authenticated user is used e.g. $user = auth()->user() I'm getting the error Class "A" not found or whatever other value is set in the type column.

Any help is greatly appreciated

Tag new release

Hey

Thanks for putting this together. Love it.

Can you tag the new release? Getting the old traits pull through with 0.5

Thanks
Lee

Type column not being set

I've created a simple project to test the functionality of parental. However, the type column is not being set when creating a child model. It is always set to null ('type' is set for fillable). If I manually pass a type when creating the model it'll be set. I shouldn't need to do that, right? It will determine the type when the model is created? What might I be doing wrong?

Add child global scopes when querying parent

It would be nice if when querying for Vehicle::all() and Car extends Vehicle, for example, the global scope for Car would apply when returning instances of Car, but wouldn't apply to instances of Plane. Thoughts?

Integration with nova-permission

I am trying to integrate this package together with nova-permission and apparently it is not compatible or something I am doing wrong.

This is my Admin model

...

use Spatie\Permission\Traits\HasRoles;
use Tightenco\Parental\HasParentModel;

class Admin extends User
{
    use HasRoles;

    use HasParentModel;
}

and my Admin resource

...

    public function fields(Request $request)
    {
        return [
            ...
            MorphToMany::make('Roles', 'roles', \Vyuldashev\NovaPermission\Role::class),
            MorphToMany::make('Permissions', 'permissions', \Vyuldashev\NovaPermission\Permission::class),
        ];
    }

...

Error: Class name must be a valid object or a string

Update readme

the trait has changed from HasParent to HasParentModel, and the readme is outdated. Also, thanks for this trait!

Class strangeness in model events

So I have a parent class Listing and a child class of EbayListing. The parent class has the HasChildren trait, and the EbayListing class has the HasParent trait.

Parent Listing class:

namespace App;

use Illuminate\Database\Eloquent\Model;

class Listing extends Model
{
  use \Tightenco\Parental\HasChildren;

  protected $childTypes = [
    'EbayListing' => \App\Marketplaces\Ebay\EbayListing::class,
    'ShopifyListing' => \App\Marketplaces\Shopify\ShopifyListing::class,
  ];
  ...
}

Child EbayListing class:

namespace App\Marketplaces\Ebay;
  
use Illuminate\Database\Eloquent\Model;

class EbayListing extends \App\Listing
{
  use \Tightenco\Parental\HasParent;
  ...
}

I wanted to set up a few model events to catch certain actions when they happen. Ok, cool, so I added this to the parent Listing class:

  /**
   * The event map for the model.
   *
   * @var array
   */
  protected $dispatchesEvents = [
    'saved'     => \App\Events\Models\Listing\ListingSaved::class,
    'updated'   => \App\Events\Models\Listing\ListingUpdated::class,
    'deleted'   => \App\Events\Models\Listing\ListingDeleted::class,
  ];

So I set up a test model event for 'deleted'. I made the event listener queueable as I wanted to test some things in the job queue. Now, the queuable delete job I'm queueing doesn't do anything, but I wanted to test things out to make sure I had the queue set up correctly.

So I used artisan to do find a Listing and invoke the delete() method on it:

>>> use App\Listing;
>>> $l = Listing::find(3095);
=> App\Marketplaces\Ebay\EbayListing {#3481
     id: 3095,
     type: "EbayListing",
     ....
     created_at: "2019-04-23 16:58:47",
     updated_at: "2019-04-23 16:58:47",
   }
>>> $l->delete();
=> true

So good, so far. It added the job to the jobs table, which is great, of course. However, the job failed when the artisan queue worker ran (which I thought was odd because the handle() method is empty. So I took a look at the failed_jobs table and saw that I got a good ol' ModelNotFoundException:

Illuminate\Database\Eloquent\ModelNotFoundException: No query results for model [App\Marketplaces\Ebay\EbayListing]. in /var/www/intelebase-dev/vendor/laravel/framework/src/Illuminate/Database/Eloquent/Builder.php:452

So, I thought, perhaps the $dispatchesEvents needed to be in the child class instead of the parent, so I set that all up, but I got the same result.

Not really sure why it is complaining, though, as App\Marketplaces\Ebay\EbayListing is a valid model, though maybe it needs a backslash in front of the App in the class path. Not sure.

Any ideas?

Can a Model be a child and a parent at the same time?

I realize this might not the best database design, but I wonder if your package could doing something with this?

I have a database with the following simplified tables:

questions
type
answers
question_id
subtype

and relationships

Question::hasOne(Answer)
Answer::belongsTo(Question)

I.e. questions can have several types, and each specific type of question has a subtype via the subtype field on Answer. The subtypes are only unique within a question type.

I was trying to setup a hierarchy of sti classes like:

class Question extends Model {
    use HasChildren;

    protected $childTypes = [
        1 => MultipleChoiceQuestion::class,
        2 => NumberQuestion::class,
        ...
    ];
}
class NumberQuestion extends Question 
{
    use HasParent;

    public function answer()
    {
        return $this->hasOne(NumberAnswer::class);
    }
}
class Answer extends Model {
    use HasChildren;
}
class NumberAnswer extends Answer {
    use HasParent;
    user HasChildren;

    protected $childColumn = 'subtype';
    protected $childTypes = [
        1 => NumberVariant1::class,
        2 => NumberVariant2::class,
        ...
    ];
}
class NumberVariant1 extends NumberAnswer 
{
    use HasParent;
}

However, this fails, since both HasParent and HasChildren contain conflicting getClassNameForRelationships methods. Is there a way to do this, or something similar without changing the database layout?

Using a child in Nova relations does not work correctly

Hi,

If you use a relationship with a child model, only using a parent nova resource, it won't work properly:
BelongsTo::make('My Child', 'child', ParentResource::class),

with ParentResource defined like so:

class ParentResource extends Resource
{
    public static $model = Parent::class;
    
    // ...
}

You get the following error : 'Class name must be a valid object or a string'

I'll submit a PR to fix this later today.

Related to #14 , maybe also to #20

withCasts does not work

Hi, I have parent model 'Transaction' and child model PurchaseOrder.

I have cast class 'ThousandSeparator'

When I retrieve data using Eloquent
$purchaseOrder = PurchaseOrder::withCasts(['total' => \App\Casts\ThousandSeparator::class]);

It simply does not work. However when I use normal model, it works.

Any ideas how to solve it?

Bypass Totally Guarded Exception in HasChildren@getChildModel

I noticed that if a model is totally guarded (it has no fillable properties and the guarded array is equal to ['*'] then getChildModel() in the HasChildren trait will throw a Mass Assignment Exception when doing actions like ParentModel::first().

You can get around this by setting your fillable array to literally anything (I currently have mine as such: protected $fillable = ['not_totally_guarded'];) and everything will work fine from what I can tell.

Not a huge inconvenience for models that you want to be totally guarded, but it would be nice if the package could do something differently to bypass this unnecessary exception.

Laravel notification users

Is there any documentation on sending a notification to a "child" model?

I am using Parental, without the "type" column on the parent. When I do this:

// The "parent"
class User extends Model
{
    use \Illuminate\Notifications\Notifiable
}
// The "child"
class Admin extends User
{
    use \Parental\HasParent;
}
$admin = Admin::first();

$admin->notify(new DatabaseNotification);

Laravel database notifications store a notifiable_id and notifiable_type in the database.

I would expect the notifiable_type to be \App\User, but in this case, its \App\Admin.

Is this intended behaviour? If so, has anyone been able to get the notification_type column to be \App\User?

Set attributes on child Models

I have created a Admin model which is the child of the User Model

class Admin extends User
{
    use \Parental\HasParent;

    protected $fillable = ['contact'];
}

I have a extra attribute for the Admin model called contact and have also created a migration for the same

Schema::create('admins', function (Blueprint $table) {
   $table->bigIncrements('id');
   $table->string('contact');
   $table->timestamps();
});

But when I am trying to insert the attribute, a entry is created in the users table but there is no entry in the admins table. I am not understanding how can I add custom attributes to child models. This is the code I am writing to create a Admin Record

 App\Admin::create([
   'name'=> "Aniket Magadum",
   "email" => "[email protected]",
   "password" => "password",
   "contact" => "8286000000"
 ]);

Import cannot be found

Hi,

I am using Laravel 5.6. I have followed the installation steps correctly, however, when I try to import any of the traits, an exception is thrown saying that the import is undefined. PhpStorm cannot find it.

My model looks exactly the same as the one from the documentation.

use Tightenco\Parental\HasParentModel;

class Admin extends User
{
    use HasParentModel;
}

I have already :

  • Removed the vendor folder and reinstalled all packages
  • Cleared the cache with php artisan cache:clear
  • Made a composer dump-autoload after reinstalling all packages

Is there anyone else dealing with the same issue ? Thanks by advance.

Date fields are missing in returned object and cannot log in

Hi,

Thanks for this package. I noticed that the $user record I got back doesn't have created_at, updated_at or any other datetime fields. But I'm not sure how to fix it. Hence raising an issue

Date fields only show up if I add them to the fillable array

& there are issues logging in when I have the ReturnsChildModels trait on my User model as well. Kept redirect back to login page. This happens even when type is null

In tests using auth:api middleware, $request->user() & auth()->user() seem to always return User too rather than their respective sub Models

FR: Determining Model from type column in relationship.

Hey,

great package, thanks a lot for this.

I'm testing it atm, and it does seem to work wonderfully. I am just wondering if it wouldn't be a good idea to allow getting the type from a relationship, rather than having to add the column to every table?

Let me elaborate:

// the main parent
class Customer extends Model {

   // type column: for example Google, Microsoft, Twitter etc. 
    protected $platform;
}

class Account extends Model {

public function customer()
    {
        return $this->belongsTo(Customer::class, 'remote_customer_id');
    }
}

class Campaign extends Model {

public function account()
    {
        return $this->belongsTo(Account::class, 'remote_account_id', 'remote_account_id');
    }

}

class Keyword extends Model {

public function account()
    {
        return $this->belongsTo(Account::class, 'remote_account_id', 'remote_account_id');
    }
}

Now all these Models under a Customer model, could be for example a TwitterKeyword, or MicrosoftCampaign etc.
Theoretically in a case like this, creating an additional type column on each of these sub-models will create a lot of unnecessary data.

Is this something you think makes sense in the scope if this project?

Laravel Nova

Hi guys, awesome package! Have you been playing around with Laravel Nova? When using BelongsTo relation in Nova the following entry appear is my laravel.log along with a 'server error' notification in the frontend.

I guess this happening as the returned class name is different from the class name for which the belongs to was called? No clue on how to solve this. Any pointers and/or tips?

[2018-08-26 19:13:13] local.ERROR: Class name must be a valid object or a string {"userId":2,"email":"redacted","exception":"[object] (Symfony\\Component\\Debug\\Exception\\FatalThrowableError(code: 0): Class name must be a valid object or a string at /Users/ngn/Sites/sandstorm/vendor/laravel/nova/src/Nova.php:272)
[stacktrace]
#0 /Users/ngn/Sites/sandstorm/vendor/laravel/nova/src/Fields/FormatsRelatableDisplayValues.php(20): Laravel\\Nova\\Nova::newResourceFromModel(Object(App\\EventMember))

Installation with laravel v 6.2 e 6.3

Hi to everyone!
Today I've tried to install the package, but had dependency issue with laravel framework version. I managed to install it with framework version 6.0.3, but not with the later ones (neither with 6.2 nore 6.3).
This is the error I had:
installationError.
installationError2

And this is my composer.json file:
composer

Observing parent doesn't observe children

Hi,

Looks like when you add an observer to a parent class, the children classes don't benefit from the observer.

It can be solved by adding the following to ReturnsChildModels on the parent class:

    public static function observe($classes)
    {
        parent::observe($classes);
        // register the observer on the children only if we're observing the parent
        if (static::class === self::class) { 
            foreach ((new self)->childTypeAliases as $child) {
                $child::observe($classes);
            }
        }
    }

This way, adding an observer to the parent will add it to all the children, and adding an observer to a child will only add it to the child.

Documentation tweak

At the beginning of the readme, the example parent class doesn't use the HasChildren trait, it's only when you get to the Type Aliases section that it becomes apparent.

It's confusing for a few minutes when you first get started with this package. Otherwise, love it!

classToAlias is not a public method

public static function bootHasParentModel()
    {
        static::creating(function ($model) {
            if ($model->parentHasReturnsChildModelsTrait()) {

                $model->forceFill(
                    [$model->getInhertanceColumn() => $model->classToAlias(get_class($model))]
                );
            }
        });
        static::addGlobalScope(function ($query) {
            $instance = new static;
            if ($instance->parentHasReturnsChildModelsTrait()) {
                $query->where($instance->getInhertanceColumn(), $instance->classToAlias(get_class($instance)));
            }
        });
    }

Enforcing Parent Class Contracts

Hey guys,

Not sure if I'm missing something here, but I'd like to be able to enforce this following behaviour in some way:

abstract class Vehicle extends Model
{
    use ReturnsChildModels;

    public abstract function start();
}
class Car extends Vehicle
{
    use HasParentModel;

    // HAS to implement parent start() method, won't run otherwise
}

If the parent model is abstract, when I call something like Vehicle::all(), an exception is thrown saying Vehicle can't be instantiated (which I guess, is kind of expected).

I could make the parent implement an interface, but if I can't make the parent abstract the parent will have to implement the method, which will not enforce the contract for its children.

Polymorphic Relationship: Parent Morph Name

Forgive me, if it seems like I am rambling. Trying to show what I want to accomplish.

It looks like parental almost does what I am looking for. The Extending Models in Eloquent blog post, almost explains what I want to do. But it's falling a bit short.

This may be the use case that #10 is looking for. Where the parent morph name should be used by the extended model. Or maybe I'm just missing something and need a better example.

I am trying to add a polymorphic relationship to an existing model from a package. To make the package work, without requiring the developer to manually add the relationship to an existing class.

class User extends Model {
    // dev has to add this to the existing model
    public function posts() {
          return $this->morphMany(Vendor\Package\Post::class);
    }
}
$user = User::first();
$user->posts()->get();  // How it normally works.

Hoping I could leave App\User untouched, I tried the following:

class User extends Model {
    //
}

class UserPostRelationship extends User {
    public function posts() {
          return $this->morphMany(Vendor\Package\Post::class);
    }
}

$user = User::first();
$user->posts()->get();  // Call to undefined method App/User::posts()

This is where I stumbled across Extending Models in Eloquent.

Maybe there's a way to push the function to App\User using boot(), but I am not sure if it's possible.


You've done a great job with STI, but it breaks existing polymorphic relationships that may already be in the parent.

class User extends Model {
    public function related() {
          return $this->morphMany(Related::class);
    }
}

class UserPostRelationship extends User {

    use Tightenco\Parental\HasParentModel;

    public function posts() {
          return $this->morphMany(Vendor\Package\Post::class);
    }
}

$user = UserPostRelationship::first();  // emulates App\User
$user->posts()->get();  // Works as expected
$user->related()->get();  // Is empty, since it's morph name is App\UserPostRelationship

HasParentModel is not as clean as I hoped. But that does not mean I can't work with it. Hopefully this helps other looking for a similar solution.

=> Illuminate\Database\Eloquent\Collection {#2963
     all: [
       App\Post {#2953
         id: 2662,
         content: null,
         user_id: 1,
         user_type: "App\UserPostRelationship",
         created_at: "2019-01-22 04:58:47",
         updated_at: "2019-01-22 04:58:47",
       },
     ],
   }

Use Case:

Here's my use case. I have two separate packages with polymorphic relationships that I would like to tie together with App\User.

  • Vendor\Package\Post::class
  • Vendor\Package\Star::class requires Vendor\Package\Post::class

and

App\User has an existing polymorphic relationship I would like to use.

$user = UserStarRelationship::first();  // Get a user
$user->stars()->get(); // Get relationships - this model only
$user->posts()->get(); // Can't see Post relationships
$user->related()->get();  // Can't see user relationships
$foo = UserPostRelationship::find($user->id); // Extra work required
$foo->posts()->get(); // But disconnected from User & Stars
$bar = User::find($user->id); // More work required
$bar->related()->get(); //  Disconnected from Posts & Stars

Maybe this is the only way it will work. But fixing the morph name could help packages tie to existing model relationships. If the base model can not be directly extended.


It also seems this is not possible. But again is could be helpful for package development and distribution.

class UserPostRelationship extends User {
    use Tightenco\Parental\HasParentModel;
}

class UserStarRelationship extends UserPostRelationship {
    use Tightenco\Parental\HasParentModel;
}

$user = UserStarRelationship::first() // Table 'user_post_relationship' doesn't exist

I applaud your geniusness

Please add this to the laravel nova repository
Novapackages.com

Oh and i love you guys and your amazing talents and skills, please add a โ€œsupport meโ€ method somewhere to your readme.

Column reference is ambiguous

Hi, I have the following problem.

I have 2 models:
InvoiceItem - parent

<?php

namespace App\Models;

use App\Enums\ProductType;
use Illuminate\Database\Eloquent\Model;
use \Tightenco\Parental\HasChildren;

class InvoiceItem extends Model {

    use HasChildren;

    protected $table = 'invoice_items';

    //...

    protected $childColumn = 'product_type';

    protected $childTypes = [
        ProductType::SUBSCRIBE => InvoiceItemSubscribe::class,
    ];
    //...

InvoiceItemSubscribe - children, have scopeForAct method

<?php

namespace App\Models;

use Carbon\Carbon;
use Tightenco\Parental\HasParent;

class InvoiceItemSubscribe extends InvoiceItem {

    use HasParent;

    //...

    /**
     * @param $query
     * @param Carbon $prevMonth
     */
    public function scopeForAct($query, Carbon $prevMonth) {
        $query
            ->select('invoice_items.*')
            ->whereDate('invoice_items.date_from', '<', $prevMonth)
            ->leftJoin('act_items', 'invoice_items.id', '=', 'act_items.invoice_item_id')
            ->groupBy('invoice_items.id')
            ->havingRaw('ceil(invoice_items.amount) > count(act_items.id)')
            ->whereHas('invoice', function ($query) {
                $query->paid();
            });
    }
}

I execute the query:

App\Models\InvoiceItemSubscribe::forAct($date)->get();

query to sql:

select "invoice_items".*
     from "invoice_items"
              left join "act_items" on "invoice_items"."id" = "act_items"."invoice_item_id"
     where "invoice_items"."date_from"::date < '2019-07-09 00:00:00'
       and exists(select *
                  from "invoices"
                  where "invoice_items"."invoice_id" = "invoices"."id"
                    and "invoice_status" = 'paid')
       and "product_type" = 'subscribe'
     group by "invoice_items"."id"
     having ceil(invoice_items.amount) > count(act_items.id)

Postgress return:
[2019-07-09 11:07:34] [42702] ERROR: column reference "product_type" is ambiguous

I wanted to fix this by adding a prefix to the table name in the $childColumn variable

protected $childColumn = 'invoice_items.product_type';

This of course solves the ambiguity problem.
But with this fix, your package stops working, when all records are requested by the parent model, instances of the parent model are returned, but the children are expected.

App\Models\InvoiceItem::all();

I expect instance

App\Models\InvoiceItemSubscribe::class

I get instance

App\Models\InvoiceItem::class

Help with relationships

Hi Guys.

I'm using parental in my app using Nova and my Users model is my parent, I have a child model (Admins) and a whole bunch of relations on that model.

I am getting errors when viewing my child model and it's relations
"SQLSTATE[42S22]: Column not found: 1054 Unknown column 'documents.user_id' in 'where clause' (SQL: select * from documentswheredocuments.user_id= 4 anddocuments.user_id is not null)"

This looks like the issue you mentioned on the blog post about relationships but i thought this package solved these issues by using the use HasParentModel trait?

So, my questions are:

  • Do I have to keep all relationships on my parent (Users) model instead of child model?
  • Or must I specify the foreign key in the relationship
  • Or is this a Nova issue that I need to work around

Thanks for the package and great work

Multiple types for the same user

Hi,
This is very nice package but for my app a user can have more then one role, a user can be an editor and a reviewer for example.
In my users table i have a roles field that is a SET
Is it possible to make this package support multiple roles.

Fillable on parent and child

I would like to create a set of polymorphic models Dynamic and Regular. They will inherit the same base table "categories". I want to use $dynamic->fill() to fill the attributes of both the parent and child model. Is this possible?

Category

class Category extends Model
{
    use Filtered, HasChildren;

    /**
     * @var array
     */
    protected $childTypes = [
        'dynamic' => Dynamic::class,
        'link' => Link::class,
        'regular' => Regular::class,
    ];

    /**
     * @var array
     */
    protected $fillable = [
        'type',
        'name',
    ];

Dynamic

class Dynamic extends Model implements CategoryType, Criteriable
{
    use HasParent;

    /**
     * @var string
     */
    protected $table = 'category_dynamics';

    /**
     * @var array
     */
    protected  $fillable = [
        'lol',
    ];
$fill = [
    'name' => "Laughing Acronyms"
    'lol' => 'rofl',
];

$dynamic = new Dynamic($fill);
$dynamic->save();

It' be cooler if it did

Empty Fillable Array Causes Mass Assignment Exception

So I'll prefix this by saying that I see the documentation says to have type (or your customized name for the column) to be a fillable property. But in practice I've noticed that not having type in your fillable column works as well (the type still gets set appropriately from my experiments).

This led me to try having a parent type without any fillable properties, where I encountered a Mass Assignment Exception asking me to add id to the fillable property (a not so great idea). This exception would be thrown any time I tried to use the model like doing basic operations that don't normally encounter mass assignment exceptions like first() and refresh()

Laravel Version: 6.3.0
Parental Version: 0.9.0

To reproduce, create a parent model without a fillable property, like so:

<?php

namespace App;

use Parental\HasChildren;
use Illuminate\Database\Eloquent\Model;

class ExampleParent extends Model
{
    use HasChildren;
}

Make sure you have a table for the model to interact with and that it has at least one row of valid data. Now try to fetch that row using the model static functions like \App\ExampleParent::first() and see that you get a mass assignment exception.

Double events when registering in parent boot

When registering an event in a parent's boot method. It is triggered twice if the parent was booted first in the same process.

For instance, with a Page model that has a Product child and a created event registered in Page::boot():

Product::create() // triggers the created event once as expected

(another process)

Page::create() // triggers the created event once as expected
Product::create() // triggers the created event twice (not expected)

I believe this was introduced in #26.

[Feature request] Merge fillable columns with parent

I was missing this feature and stumbled upon this issue: #77

After reading the issue and the comments I decided to create this feature request and introduce a potential way of implementing this.

This would be nice feature to have.

I've created a trait in my project based off of @litvinjuan's answer in the issue:

<?php

namespace App\Concerns;

trait ExtendParentFillables
{
    public function getFillable()
    {
        $parentFillable = (new \ReflectionClass($this))->getParentClass()->newInstance()->getFillable();

        return array_merge($this->fillable, $parentFillable);
    }
}

This could be included in the package and to prevent breaking changes this could be added in the form of a trait as well. Which would make it opt-in and opens the potential for a "guarded" form as well.

The trait is not only useful in parental's context as it can be used to extend any models' fillables.

What names should be stored when children inherit polymorphic relations?

To provide some context, say there's a Vehicle model with a polymorphic relation to Engine which has an...enginable_type column, for lack of a good name. If Vehicle is extended by Car or Plane, right now the engineable_type will contain the Car and Plane names by default if an engine is associated to one of them instead of Vehicle.

My question is, would it make sense to override this behavior and have the children use the parent model's morph name? My main thinking is that if you're dropping this into an existing application, storing the morph name of the child model subtly changes the behavior of the system and seems like it could be an unintended side effect. What I have in mind is something as simple as this:

public function getMorphClass()
{
    return array_search($this->getParentClass(), Relation::morphMap());
}

Documentation out of date

I installed the package doing composer require "tightenco/parental=0.5"

The trait names are not longer valid? When I use HasParent or HasChildren trait I get an error saying:

Trait 'Tightenco\Parental\HasParent' not found

I had to use the following ones:

use \Tightenco\Parental\HasParentModel and use \Tightenco\Parental\ReturnsChildModels;

Another issue: custom aliases doesn't work. I put this on my parent model:

protected $childTypes = [
        'order' => App\Order::class,
        'comeback' => App\Comeback::class,
    ];

And on my table, I get App\Order and App\Comeback as a type

Can't associate records using save

If I want to do something like this

return $this->tracks()->save(new MyTrack());

where the parent is trying to create a related track item, it will fail because using new to create an instance of MyTrack will not fill in the type attribute. This currently works with create as follows.

return MyTrack::create(['refund_id' => $this->id]);

as the newInstance method is getting overridden. The suggestion then would be to eliminate the newInstance method, and instead move its logic into the bootHasParentModel inside a static::saving(function ($model) {}) so that we could handle this for both the static create as well as the hasMany->save() situation.

Is there a way to filter query results by `getInheritanceColumn` when the parent is also a child

Love the idea of the package but I'm running into some issues.
I have two classes, Product and Subscription and product type points to itself (I saw it in the test models with the Car, Truck and Vehicle).

class Product extends Model {
    use HasFactory, HasChildren;
    
    public static function childTypes():array
    {
        return [
            'product' => self::class,
            'subscription' => Subscription::class
        ];
    }
}
class Subscription extends Product
{
    use HasParent;
}

When I use Product::all() the result is filled with products and subscriptions and when I use Subscription::all() the result is filled only with subscription. I saw that HasParent registers global scope which filters by the $this->getInheritanceColumn() and I can always implement such logic in the Product model but I was wondering Is there any method or scope that I'm missing?

Infinite recursion when setting parent in childTypes

Hello
To make myself clear look at the following example

class User extends Model
{
    use \Parental\HasChildren;
    protected $childTypes = [
        'admin' => App\Admin::class,
        'guest' => App\Guest::class,
        'simple' => App\User::class, // parent as child
    ];
}

At this line
src/HasChildren.php(22): $childClass::registerModelEvent($event, $callback);
an infinite recursion occurs because the parent is added to the child types.

I was already using this configuration with the previous tightenco/parental and now I moved to this package.
You can fix this by adding a check right above that line so the function will look like this

protected static function registerModelEvent($event, $callback)
    {
        parent::registerModelEvent($event, $callback);

        if (static::class === self::class && property_exists(self::class, 'childTypes')) {
            // We don't want to register the callbacks that happen in the boot method of the parent, as they'll be called
            // from the child's boot method as well.
            if (! self::parentIsBooting()) {
                foreach ((new self)->childTypes as $childClass) {
                    if ($childClass == self::class) // checking for self-inheritance
                        continue; 
                    $childClass::registerModelEvent($event, $callback);
                }
            }
        }
    }

Are you going to add a fix like this in the next version?
Do you think there are any other problems with self inheritance?
Thank you for you time

What's the best way to add a fillable to a child Model?

Let's say I have this:

namespace App;

use Illuminate\Notifications\Notifiable;
use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Foundation\Auth\User as Authenticatable;

class User extends Authenticatable
{
    use \Tightenco\Parental\HasChildren;
    use Notifiable;

    protected $childTypes = [
        'teacher' => App\Teacher::class,
    ];

    /**
     * The attributes that are mass assignable.
     *
     * @var array
     */
    protected $fillable = [
        'first_name', 'last_name', 'email', 'password', 'type'
    ];
}
namespace App;

use Illuminate\Database\Eloquent\Model;

class Teacher extends User
{
    use \Tightenco\Parental\HasParent;

    //
}

What would be the best way to add a school fillable to my Teacher model? Do I have to redefine the whole $fillable?

childTypes don't seem to work

Running into the same issue as reported in #34 regarding aliases. The way the documentation reads (to me at least), I understand it to work as follows:

In base/parent model:

protected $childTypes = [
        'EbayListing' => App\Marketplaces\Ebay\EbayListing::class,
        'ShopifyListing' => App\Marketplaces\Shopify\ShopifyListing::class,
    ];

When I create a new EbayListing record and save it, I still get the fully qualified class name instead of just "EbayListing" as the value in the type column.

Using 0.7 ... any ideas? Thanks in advance! This is a terrific idea for a package, by the way :)

Registering an observer on the parent class with a self referencing child throws `Maximum function nesting`

When you register an observer on the parent class that has self as childType it throws Error : Maximum function nesting level of '256' reached, aborting!.

Example

<?php

class User
{
    use \Tightenco\Parental\HasChildren;

    protected $childTypes = [
        'super_admin' => Admin::class,
        'admin' => self::class
    ];
}
// AppServiceProvider.php

class AppServiceProvider
{
    public function boot(Environment $environment)
    {
          User::Observe(UserObserver::class);
    }
}

Solution

This line should check if $childClass is not an instance of self.

foreach ((new self)->childTypes as $childClass) {
    if ($childClass !== self::class) {
        $childClass::registerModelEvent($event, $callback);
    }
}

Crash with child relationship and SerializesModels

What happens

When a child model has a custom relationship, it crashes when sending a notification that uses the SerializesModels trait. Only the broadcast channel is concerned, both email and database channels work fine. It happens when the relationship object has been already used or loaded with the with method.

Environment

Laravel 5.8 and Parental 0.7.

Steps to reproduce

Here is a dummy example.

I have an Article parent model, which can either be of type OriginalArticle or SourcedArticle. Only SourcedArticle items have a relationship with the sources table.

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;

class Article extends Model
{
    use \Tightenco\Parental\HasChildren;

    protected $fillable = ['type'];

    public function comments()
    {
        return $this->hasMany(Comment::class);
    }
}
<?php

namespace App\Models;

class SourcedArticle extends Article
{
    use \Tightenco\Parental\HasParent;

    public function source()
    {
        return $this->hasOne(Source::class, 'id');
    }
}

And here is the Comment model:

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;

class Comment extends Model
{
    public function article()
    {
        return $this->belongsTo(Article::class);
    }
}

Now let's say that we want to send a broadcast notification when someone commented a sourced article.

$user->notify(new UserCommentedSourcedArticleNotification($comment));
<?php

namespace App\Notifications;

use App\Models\Comment;
use Illuminate\Notifications\Notification;

class UserCommentedSourcedArticleNotification extends Notification
{
    private $comment;

    public function __construct(Comment $comment)
    {
        $this->comment = $comment;
    }

    public function via()
    {
        return ['broadcast'];
    }

    public function toBroadcast()
    {
        return [
            'source_link' => $this->comment->article->source->link
        ];
    }
}

Then on sending, this error will happen:

Illuminate\Database\Eloquent\RelationNotFoundException
Call to undefined relationship [source] on model [App\Models\Article].

Causes

The problem is that the relationship is not saved correctly on serializing. Instead of keeping the child type of the relationship, the SerializesModels trait stores the article relation with the parent Article type, instead of SourcedArticle in that case. After unserializing, it tries to access to a relationship that isn't part of the parent model.

I hope I have been clear enough, thanks for your support!

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.