25

I have a laravel project, and I need to make some calculations immediately after I save a model and attach some data to it.

Is there any event that is triggered in laravel after calling attach (or detach/sync)?

Mihai Crăiță
  • 3,328
  • 3
  • 25
  • 37

4 Answers4

28

No, there are no relation events in Eloquent. But you can easily do it yourself (Given for example Ticket belongsToMany Component relation):

// Ticket model
use App\Events\Relations\Attached;
use App\Events\Relations\Detached;
use App\Events\Relations\Syncing;
// ...

public function syncComponents($ids, $detaching = true)
{
    static::$dispatcher->fire(new Syncing($this, $ids, $detaching));

    $result = $this->components()->sync($ids, $detaching);

    if ($detached = $result['detached'])
    {
        static::$dispatcher->fire(new Detached($this, $detached));
    }

    if ($attached = $result['attached'])
    {
        static::$dispatcher->fire(new Attached($this, $attached));
    }
}

event object as simple as this:

<?php namespace App\Events\Relations;

use Illuminate\Database\Eloquent\Model;

class Attached {

    protected $parent;
    protected $related;

    public function __construct(Model $parent, array $related)
    {
        $this->parent    = $parent;
        $this->related   = $related;
    }

    public function getParent()
    {
        return $this->parent;
    }

    public function getRelated()
    {
        return $this->related;
    }
}

then a basic listener as a sensible example:

    // eg. AppServiceProvider::boot()
    $this->app['events']->listen('App\Events\Relations\Detached', function ($event) {
        echo PHP_EOL.'detached: '.join(',',$event->getRelated());
    });
    $this->app['events']->listen('App\Events\Relations\Attached', function ($event) {
        echo PHP_EOL.'attached: '.join(',',$event->getRelated());
    });

and usage:

$ php artisan tinker

>>> $t = Ticket::find(1);
=> <App\Models\Ticket>

>>> $t->syncComponents([1,3]);

detached: 4
attached: 1,3
=> null

Of course you could do it without creating Event objects, but this way is more convenient, flexible and simply better.

Jarek Tkaczyk
  • 78,987
  • 25
  • 159
  • 157
  • thanks great answer! so I will have to use my new defined function (syncComponents in your example) with my model when sync-ing? – Mihai Crăiță Mar 10 '15 at 21:03
  • ok, this is in fact what I wanted to avoid, to rewrite every file where attach/sync is called. But you gave me very good answer mate! – Mihai Crăiță Mar 11 '15 at 11:21
12

Steps to solve your problem:

  1. Create custom BelongsToMany relation
  2. In BelongsToMany custom relation override attach, detach, sync and updateExistingPivot methods
  3. In overriden method dispatch desired events.
  4. Override belongsToMany() method in Model and return your custom relation not default relation

and that's it. I created package that already doing that: https://github.com/fico7489/laravel-pivot

fico7489
  • 7,931
  • 7
  • 55
  • 89
12

Laravel 5.8 now fires events on ->attach()

Check out: https://laravel.com/docs/5.8/releases

And search for: Intermediate Table / Pivot Model Events

https://laracasts.com/discuss/channels/eloquent/eloquent-attach-which-event-is-fired?page=1

Twostein
  • 136
  • 1
  • 2
4

Update:

From Laravel 5.8 Pivot Model Events are dispatched like normal model.

https://laravel.com/docs/5.8/releases#laravel-5.8

You just need to add using(PivotModel::class) to your relation and events will work on the PivotModel. Attach($id) will dispatch Created and Creating

Detach($id) will dispatch Deleting and Deleted,

Sync($ids) will dispatch the needed events too [Created,Creating,Deleting,Deleted]

Only dispatch() with out id doesn't dispatch any event until now.

Amr Ezzat
  • 341
  • 3
  • 12