Parental Documentation

repository·main·Indexed 23 days ago

https://github.com/tighten/parental

A Laravel package that implements Single Table Inheritance (STI) for Eloquent models, allowing multiple model classes to share a single database table. It provides the HasChildren and HasParent traits, a become() method for transitioning model types, and specialized eager loading methods like loadChildren() and childrenWith() for child-specific relationships.

Tokens
1.7K
Snippets
6
Records
8
Agent score
32%

What's inside Parental

  1. How Single Table Inheritance works with Parental

    main

    Parental allows you to extend a model to add specific behavior while referencing the same database table.

    • Use the HasChildren trait on the parent model to allow it to act as a base for multiple child types and to enable automatic instantiation of child models when querying the parent.
    • Use the HasParent trait on child models to tell Eloquent to use the parent's table instead of looking for a table named after the child class.

    This solves the problem where Laravel normally expects a separate table for every model class.

    // The "parent"
    class User extends Model
    {
        use HasChildren;
    }
    
    // The "child"
    class Admin extends User
    {
        use HasParent;
    
        public function impersonate($user) {
            //...
        }
    }
    
    // Returns "Admin" model, but references "users" table:
    $admin = Admin::first();
    $admin->impersonate($user);
  2. Enable Laravel Nova support for Parental

    main

    To allow Laravel Nova resources to be shared between parent and child models, register the Parental\Providers\NovaResourceProvider in your NovaServiceProvider's boot method.

    class NovaServiceProvider extends NovaApplicationServiceProvider
    {
        public function boot() {
            parent::boot();
            // ...
            $this->app->register(\Parental\Providers\NovaResourceProvider::class);
        }
    }
  3. Configure the type column and child aliases

    main

    To use Parental, your parent table must have a column (defaulting to type) that stores the class name or an alias of the child model.

    Customizing the type column

    Set the $childColumn property on the parent model to use a different column name.

    Using Type Aliases

    To avoid storing full PHP class names in your database, use the $childTypes property on the parent model to map short aliases to class names. This decouples your database from your application structure.

    Example Configuration

    class User extends Model
    {
        use HasChildren;
    
        protected $fillable = ['parental_type'];
    
        // Use a custom column name
        protected $childColumn = 'parental_type';
    
        // Use aliases instead of full class names
        protected $childTypes = [
            'admin' => Admin::class,
            'guest' => Guest::class,
        ];
    }
  4. Eager load child model relationships

    main

    Requirement

    Eager-loading relationships on child models is only supported on Laravel 11 and above.

    Eager loading from a Model instance or Collection

    Use loadChildren() to eager-load specific relationships for specific child types. Use loadChildrenCount() to eager-load relationship counts (e.g., mentions_count).

    Eager loading from a Query or Relationship

    Use childrenWith() on a query builder or relationship to eager-load child-specific relationships during the initial fetch. Use childrenWithCount() for counts.

    Example Usage

    // From a model instance
    $message = Message::first();
    $message->loadChildren([
        TextMessage::class => ['mentions'],
        ImageMessage::class => ['attachments'],
    ]);
    
    // From a query
    $messages = Message::query()->childrenWith([
        TextMessage::class => ['mentions'],
        ImageMessage::class => ['attachments'],
    ])->get();
    
    // From a relationship
    $room = Room::first();
    $messages = $room->messages()->childrenWith([
        TextMessage::class => ['mentions'],
        ImageMessage::class => ['attachments'],
    ])->get();
  5. Transform models between types with `become()`

    main

    You can transition a model from one child type to another using the become() method. This method returns a new instance of the target child model with the original attributes preserved.

    Note: You must call save() on the returned instance to persist the change to the database.

    You can listen for the transition using the becoming method on the target child class.

    // Retrieve a pending order
    $order = Order::first();
    
    // Ship the order by transforming it
    $order = $order->become(ShippedOrder::class);
    
    // Updates the "type" column to "shipped" and returns a ShippedOrder instance
    $order->save();
    
    // Listen for the transition
    ShippedOrder::becoming(function ($shippedOrder) {
        // Do something before the model is saved...
    });
  6. Eager load child models using loadChildren()

    main

    The loadChildren macro allows you to eager load specific relationships on a collection of models that may contain different child classes (Single Table Inheritance). It accepts a map where keys are the class names of the models and values are arrays of relationship names to load for that specific class.

    Example usage:

    $collection->loadChildren([
        ChildClassA::class => ['relationA'],
        ChildClassB::class => ['relationB'],
    ]);
  7. Eager load child model counts using loadChildrenCount()

    main

    The loadChildrenCount macro allows you to eager load the counts of specific relationships on a collection of models that may contain different child classes. It accepts a map where keys are the class names of the models and values are arrays of relationship names to count for that specific class.

    Example usage:

    $collection->loadChildrenCount([
        ChildClassA::class => ['relationA'],
        ChildClassB::class => ['relationB'],
    ]);