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.