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

Time:12-07

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)

CodePudding user response:

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.

  • Logs table (includes, id, application_id, service, etc. columns)
  • Events table (includes id, event_id, eventable_id, eventable_type)
  • Multiple "Eventable" tables - Transition, Decision, Message, etc. (these don't need any special columns other than what you need to store your data)

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.

  • Related