I have a Sample model that has a status (string) and a current task (foreign key to the Task model).
field type
---------------------------
id int
status string -> I use an Enum to store possible values
current_task_id int -> foreign key to the Task model
On my model, I define the relationship as follows:
public function currentTask()
{
return $this->belongsTo(Task::class, 'current_task_id', 'id');
}
Now, I've created an Observer with the following function:
public function updated(Sample $sample)
{
// check if the current task is null and if not change the status to in progress
Log::info('Sample status changed to in progress', ['sample' => $sample->toArray()]);
if ($sample->currentTask()->exists()) {
$sample->status = 'in progress';
$sample->save();
}
}
I want this to trigger when a sample is updated, check if there is an associated Task, and change the status to in progress if so.
I've encountered two issues:
- When updating the current_task_id field manually and running save(), I get a memory leak caused somehow by the observer code.
- When running the "associate" method to assign the currentTask, the observer does not trigger.
See the code below that I run in Tinkerwell
$sample = Sample::factory()->create();
echo $sample->currentTask(); // null
echo $sample->status; // not started
$sample->current_task_id = 2;
$sample->save(); // memory leak, additionally, if I check $sample->currentTask it gives me null...
Or with associate:
$sample = Sample::factory()->create();
echo $sample->currentTask(); // null
echo $sample->status; // not started
$sample->currentTask()->associate(2); // does not trigger observer
echo $sample->currentTask(); // Task object
echo $sample->status; // not started
How can I trigger the observer on associate? Or alternatively, why would the save() cause a memory leak?
CodePudding user response:
Here's my recommendation:
Keep using an observer but use the saving (or updating) method:
public function saving(Sample $sample)
{
// check if the current task changed
Log::info('Sample status changed to in progress', ['sample' => $sample->toArray()]);
if ($sample->isDirty('current_task_id') && CurentTask::where('id', $sample->current_task_id)->exists()) {
$sample->status = 'in progress';
}
}
If you don't use the isDirty
check in a saving
or updating
event you will end up with an infinite loop of saving, triggering updated
and saving again