cvsouth/eloquent-inheritance

Joined-table inheritance for Laravel

1.0.22 2022-02-07 23:51 UTC

README

Installation

composer require cvsouth/eloquent-inheritance
php artisan migrate

Usage

Defining classes

Extend your models from InheritableModel instead of the usual Model:

class Animal extends InheritableModel
{
    public $table = "animals";
    
    protected $fillable =
    [
        'name',
        'species',
    ];
    
    public function speak()
    {
        print($this->name . ' makes a noise');
    }
}
class Bird extends Animal
{
    public $table = "birds";
    
    protected $fillable =
    [
        'flying',
    ];

    public function speak()
    {
        print('AAA!');
    }

    public function fly()
    {
        $this->flying = true;
    }

    public function land()
    {
        $this->flying = false;
    }

    public function isFlying()
    {
        return $this->flying;
    }
}

When creating your migrations add base_id:

class CreateAnimalsTable extends Migration
{
    public function up()
    {
        Schema::create('animals', function (Blueprint $table)
        {
            $table->bigIncrements('id');
            $table->bigInteger('base_id')->unsigned();
            $table->string('species', 250);
            $table->string('name', 250)->nullable();
        });
    }

    public function down()
    {
        Schema::drop('animals');
    }
}
class CreateBirdsTable extends Migration
{
    public function up()
    {
        Schema::create('birds', function (Blueprint $table)
        {
            $table->bigIncrements('id');
            $table->bigInteger('base_id')->unsigned();
            $table->boolean('flying')->default(false);
        });
    }

    public function down()
    {
        Schema::drop('birds');
    }
}

Storing objects

You can then use your objects just like normal Eloquent objects:

$bird = new Bird
([
    "species" => "Aratinga solstitialis", // Note: This attribute is inherited from Animal
]);

$bird->fly();
echo "This " . strtolower($bird->species) . " is " . ($bird->isFlying() ? "" : "not ") . "flying" . '<br/>';
// This aratinga solstitialis is flying

$bird->species = 'Sun Conure';

$bird->save();

$bird->land();
echo "This " . strtolower($bird->species) . " is " . ($bird->isFlying() ? "" : "not ") . "flying" . '<br/>';
// This sun conure is not flying

Querying objects

Again, you can query the object just like usual for Eloquent:

$bird = Bird::where("species", "=", "Aratinga solstitialis")->first();

$bird->speak();
// AAA!

Primary keys at different levels of inheritance

At each level of inheritance the object has an id. In the example above, the $bird has an animal id as well as a bird id In addition to this each model has a common id called base_id which is consistent throughout it's class hierarchy.

Use the id_as method to get the id for a model at a specific level of inheritance:

// The model's animal id
echo $bird->id_as(Animal::class);

// The model's bird id
echo $bird->id_as(Bird::class);

Or use the base_id property to get the entities base id:

// The model's base id
echo $bird->base_id

Relationships

Relationships work like regular eloquent relationships but bear in mind that you can reference specific levels of inheritance. For example:

class Trainer extends InheritableModel
{
    public $table = "trainers";
    protected $fillable =
    [
        'name',
        'animal_id',
    ];
    
    public function animal()
    {
        return $this->belongsTo(Animal::class);
    }
}
class CreateTrainersTable extends Migration
{
    public function up()
    {
        Schema::create('trainers', function (Blueprint $table)
        {
            $table->bigIncrements('id');
            $table->bigInteger('base_id')->unsigned();
            $table->string('name', 250)->nullable();
            $table->bigInteger('animal_id')->unsigned();
        });
        
        Schema::table('trainers', function ($table)
        {
            $table->foreign('animal_id')->references('id')->on('animals')->onDelete('cascade');
        });
    }

    public function down()
    {
        Schema::drop('trainers');
    }
}
$bird = Bird::where("species", "=", "Aratinga solstitialis")->first();
$trainer = new Trainer
([
    "name" => "Trainer 1",
    "animal_id" => $bird->id_as(Animal::class), // Reference the bird's Animal ID
]);
$trainer->save();

echo class_basename($trainer->animal); // Bird
echo $trainer->animal->species; // Sun Conure