laradan's avatar

Laravel 5.8 and syncing/attaching/detaching events.

Laravel 5.8 is supposed to dispatch the syncing, attaching and detaching events (https://laravel.com/docs/5.8/releases --> Intermediate Table / Pivot Model Events section).

I tried it out but the following code throws the exception Call to undefined method App\Task::syncing()

    // Task Model
    class Task extends Model
    {
        public static function boot()
        {
            parent::boot();
    
            static::syncing(function (Task $task) {
                dd('syncing event has been fired!');
            });
        }
    
        public function project()
        {
            return $this->belongsTo(Project::class);
        }
    }

    // Project Model    
    class Project extends Model
    {
        public function tasks()
        {
            return $this->hasMany(Task::class);
        }
    }

    // web.php
    $project = \App\Project::first();
    $project->tasks()->sync([1,2]);

NOTE: since Laravel 5.8 is supposed to dispatch the syncing event I don't want to use an external package.

0 likes
6 replies
bobbybouwmann's avatar

@laradan This only works in the intermediate model of the many-to-many relationship.

I have two questions here. Your relationship is currently one-to-many between Task and Project (hasMany and belongsTo) instead of many-to-many (belongsToMany and belongsToMany).

So you first need to figure out if you want one-to-many or many-to-many.

If you decide on many-to-many you can follow the following steps. As pointed out in the documentation you need to use a pivot model to achieve this. So something like this

class Task extends Model
{
    public function projects()
    {
        return $this->belongsToMany(Project::class)->using('App\ProjectTask');
    }
}

class Project extends Model
{
    public function tasks()
    {
        return $this->belongsToMany(Task::class)->using('App\ProjectTask')
    }
}

use Illuminate\Database\Eloquent\Relations\Pivot;

class ProjectTask extends Pivot
{
    public static function boot()
    {
        parent::boot();

        static::syncing(function ($item) {
            dd('syncing event has been fired!');
        });
        }
}
2 likes
laradan's avatar

Thank you @bobbybouwmann for your reply.

I applied the changes you suggested to me: I'm getting the same error but now on the ProjectUser class: Call to undefined method App\ProjectUser::syncing()

Note that I changed the tasks with users to give to the example more sense.

class User extends Model
{
    public function projects()
    {
        return $this->belongsToMany(\App\Project::class)->using(\App\ProjectUser::class);
    }
}

class Project extends Model
{
    public function users()
    {
        return $this->belongsToMany(\App\User::class)->using(\App\ProjectUser::class);
    }
}

class ProjectUser extends Pivot
{
    public static function boot()
    {
        parent::boot();

        static::syncing(function ($item) {
            dd('syncing event has been fired!');
        });
    }
}

// web.php
$project = \App\Project::first();
$project->users()->sync([1,2]);
bobbybouwmann's avatar

Mmh, I would expect that the syncing boot method would be on the intermediate model. Can you try it on the User or Project model instead? That might be it!

laradan's avatar

I just tried and I get the same exception:

Call to undefined method App\User::syncing()

or

Call to undefined method App\Project::syncing()

laradan's avatar

Thanks @bobbybouwmann finally I figured out (thanks to the community) the problem. The release notes were misleading and they have been changed https://github.com/laravel/docs/pull/5096 .

On Laravel 5.8, when you are using the methods sync, attach or detach is going to be fired the appropriate model events (creating, updating, saving, ...) for the called action. Note that using sync, attach or detach is not going to fire any event like syncing, attaching or detaching.

More specifically, the sequence of events fired for each element passed to the sync method are:

  • saving
  • creating
  • created
  • saved

The sequence of events fired for each element passed to the attach method are:

  • saving
  • creating
  • created
  • saved

The sequence of events fired for each element passed to the detach method are:

  • deleting
  • deleted

So if you want to observe the syncing operation you actually have to observe the saving (or saved) event:

class ProjectUser extends Pivot
{
    public static function boot()
    {
        parent::boot();

        static::saving(function ($item)  {
            // this will die and dump on the first element passed to ->sync()
            dd($item);
        });
    }
}

A working example https://github.com/danielefavi/laravel-issue-example

More info on this issue https://github.com/laravel/framework/issues/28050

https://github.com/laravel/docs/pull/5096

3 likes

Please or to participate in this conversation.