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)
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.