Search code examples
laraveleloquentpolymorphism

Polymorphic, many-to-one relationships in Laravel


I am having some trouble wrapping my head around how to structure this, as it seems to be the "inverse" of all of the Laravel documentation and other examples I've found. I'm probably over-thinking it.

All of the examples I've found assume that you have one fixed datatype (e.g. Comment) that can then be attached to any number of different 'owners'. In my case, I want to have one 'owner' that can then be related to an arbitrary number of different types.

Consider a situation where:

  • We begin with an Entity
  • An Entity can have any number of Components
  • Components can be one of two or three dozen different possible types, each holding their own data (in their own database table), but with a common accessor interface
  • A given Entity may have 0 components, 1 components, or 24 components

We will want to be able to retrieve an Entity, enumerate its Components, and access them.

I think I have the right database schema, but cannot figure out how to craft the classes.

        Schema::create('entities', function (Blueprint $table) {
            $table->id();

            /* ... other boring stuff */

            $table->timestamps();
        });

       Schema::create('components_entities', function (Blueprint $table) {
            $table->id();

            $table->foreignIdFor(Entity::class);
            $table->string('component_type');
            $table->foreignId('component_id');

            $table->timestamps();
        });

        /* Example of type of component - class App\Models\Components\HaaProperties */
        Schema::create('ecd_propbag', function (Blueprint $table) {
            $table->id();
            $table->json('bag');
            $table->timestamps();
        });

        /* Example of type of component - class App\Models\Components\IsRecoverable */
        Schema::create('ecd_recoverable', function (Blueprint $table) {
            $table->id();
            $table->integer('recovery_time');
            $table->integer('recovery_cost');

            $table->timestamps();
        });

Given this structure, how do I set up the relations at the class level?


Solution

  • From what I understand, an Entity can have many Components and Components are of many types

    //Component Model
    public function type()
    {
        return $this->hasOne('App\Models\Components\Type');
    }
    public function entity()
    {
        return $this->belongsTo('App\Models\Components\Entity');
    }
    
    
    
    //Entity Model
    public function components()
    {
        return $this->hasMany('App\Models\Components');
    }
    

    To retrieve components of an Entity

    Entity::all()->first()->components()