Skip to content
Use single table inheritance in your Laravel app
PHP
Branch: master
Clone or download
calebporzio Merge pull request #47 from tikkibar/master
Support for standalone eloquent
Latest commit 4876192 Dec 28, 2019
Permalink
Type Name Latest commit message Commit time
Failed to load latest commit information.
.github Create FUNDING.yml Dec 13, 2019
src Add comment Dec 28, 2019
tests Merge pull request #61 from BertvanHoekelen/fix-observer-on-self-refe… Dec 28, 2019
.gitignore Support Laravel 6.0 Aug 30, 2019
composer.json Merge branch 'master' into tikkibar-master Dec 28, 2019
parental-banner.png Update banner image Jun 8, 2018
phpunit.xml Add unit test for trait Jul 28, 2017
readme.md Update README Oct 16, 2019

readme.md

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 "calebporzio/parental=0.9"

Simple Usage

// The "parent"
class User extends Model
{
    //
}
// The "child"
class Admin extends User
{
    use \Parental\HasParent;

    public function impersonate($user) {
        ...
    }
}
// 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();
});
// The "parent"
class User extends Model
{
    use \Parental\HasChildren;

    protected $fillable = ['type'];
}
// A "child"
class Admin extends User
{
    use \Parental\HasParent;
}
// Another "child"
class Guest extends User
{
    use \Parental\HasParent;
}
// 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.

class User extends Model
{
    use \Parental\HasChildren;

    protected $fillable = ['type'];

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

Now, running Admin::create() will set the type column in the users table to admin instead of App\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.

class User extends Model
{
    use \Parental\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.

You can’t perform that action at this time.