Home > database >  Laravel - Associate vs. Setting the ID for Observer Events
Laravel - Associate vs. Setting the ID for Observer Events

Time:12-27

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:

  1. When updating the current_task_id field manually and running save(), I get a memory leak caused somehow by the observer code.
  2. 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

  • Related