laravellaravel-relations

How do I set up Laravel Polymorphic Relations for Logs that have multiple types of events


I have an application where I need to store a one to many relation that may have multiple types.

The use case is a log model, that has multiple events. However the event can be of multiple types, let's say for example transition, decision or message. I imagine I probably want to use polymorphic relationships here but I cannot get my head around how to set this out both in the Model and in the database.

Any pointers from anyone who has done similar would be awesome! TIA!

Current Model Layout:

Application
+ id
+ name

-----

Log
+ id
+ application_id
+ {others}

-----

Transition (similar format for other event types)
+ id
+ from
+ to
+ {others}

public function eventable() {
    return $this->morphTo();
}

What I have tried

Event
+ id
+ name

-----

Eventables
+ event_id
+ eventable_id
+ eventable_type


When I then try

       $log = Log::create([
            'id' => Str::uuid(),
            'application_id' =>1,
            'service' => 'Service',
            'source' => 'Source',
            'timestamp' => Carbon::now()->setTimezone('Europe/London')->format('Y-m-d H:i:s.v'),
            'appid' => 'appid',
            'traceid' => 'traceid',
            'requestid' => 'requestid',
            'sessionid' => 'sessionid',
            'locale' => 'locale',
            'seqid' => 1,
            'partition' => 1,
            'offset' => 0,
            'request' => 'request',
            'response' => 'response',
            'data' =>  'data',
        ]);

        $event = Transition::create([
            'from' => 'from',
            'from_name' => 'from_name',
            'from_type' => 'from_type',
            'to' => 'to',
            'to_name' => 'to_name',
            'to_type' => 'to_type',
        ]);

        $event2 = Transition::create([
            'from' => 'from',
            'from_name' => 'from_name',
            'from_type' => 'from_type',
            'to' => 'to',
            'to_name' => 'to_name',
            'to_type' => 'to_type',
        ]);

        $log->events()->saveMany([
            $event, $event2
        ]);

I get

Column not found: 1054 Unknown column 'eventable_id' in 'field list' (SQL: update `transitions` set `eventable_id` = 3cc1308e-7539-43ee-8296-15fe5e317c6a, `eventable_type` = App\Models\Log, `transitions`.`updated_at` = 2022-12-05 15:53:02 where `id` = 19)

Solution

  • You can't do a polymorphic relationship between a single model of a constant type and many models of many different types if you want to use a single relationship function. You'll need to have an intermediary model with a one-to-one polymorphic relationship.

    To do this, you'll need 3 types of tables.

    Your Logs model would look something like this:

    public class Log extends Model{
        public function events(){
            return $this->hasMany(Event::class);
        }
    }
    

    The Events class would look something like this

    public class Event extends Model{
        public function eventable(){
            return $this->mprphTo();
        }
        public function log(){
            return $this->belongsTo(Log::class);
        }
    }
    

    The Transition class would look something like this

    public class Transition extends Model{
        public function comments()
        {
            return $this->morphOne(Event::class, 'eventable');
        }
    }
    

    To load any of the eventable objects, you'd first load the events relationship and then query the eventable relationship on each Event object. You reduce the number of queries by eager-loading using $logsQuery->with('events.eventable').

    If you need each eventable object to have a direct relationship with the Logs model, the only solution I can think of is to have separate transitions, decisions, and messages relationships.