Home > Mobile >  Laravel Observers and child/grandchild relationships
Laravel Observers and child/grandchild relationships

Time:03-04

I'm not sure if I'm just misunderstanding how Observers work in Laravel or if I'm doing something incorrectly. I'm currently running on Laravel 6, though this application is currently in the process of being upgraded by another team.

What I have:

  • Parent model, called Parent
  • Parent observer, called ParentObserver
  • Child model, called Child
  • Child observer, called ChildObserver
  • Grandchild model, called Grandchild
class Parent extends Model
{
    public function children()
    {
        return $this->hasMany( Child::class );
    }
}
class Child extends Model
{
    public function grandchild()
    {
        return $this->hasOne( Grandchild::class );
    }
}

Both of my observers are declared in a service provider, and both are called successfully. My question, or possibly misunderstanding, is about how observers interact.

Both of my observers have functionality that runs when a delete command is received.

class ParentObserver
{
    public function delete( Parent $parent )
    {
        $parent->children()->delete();
    }
}
class ChildObserver
{
    public function delete( Child $child )
    {
        $child->grandchild()->delete();
    }
}

The ChildObserver delete function works 100% as expected. When I delete a child, the grandchild also gets deleted. What I'm confused about is when I delete a Parent. The Parent is deleted, and the children are deleted, but the grandchildren are not. I am not directly calling to delete the grandchildren in ParentObserver, I was expecting that the ChildObserver functionality would get triggered by the actions taken in ParentObserver.

Did I miss something when setting up the observers? Or do actions taken in an observer not trigger other observers that would normally be engaged by those actions if they were in another part of the code?

CodePudding user response:

This is doing a mass delete query. It doesn't emit a deleted event for each of the Parent's children.

class ParentObserver
{
    public function deleted( Parent $parent )
    {
        $parent->children()->delete();
    }
}

It should work if you change to this:

class ParentObserver
{
    public function deleted( Parent $parent )
    {
        $parent->children()->get()->each(function (Child $child) {
            $child->delete();
        });
    }
}

Possible improvements:

  • The cursor method was implemented in laravel 6, so you could use it instead of get to reduce memory usage
  • You could use Higher Order Messages with the each method to make the code simpler.
class ParentObserver
{
    public function deleted( Parent $parent )
    {
        $parent->children()->cursor()->each->delete();
    }
}

CodePudding user response:

From Laravel's documentation:

When issuing a mass update or delete query via Eloquent, the saved, updated, deleting, and deleted model events will not be dispatched for the affected models. This is because the models are never actually retrieved when performing mass updates or deletes.

So, if you are doing mass delete or mass update it won't trigger.

  • Related