Eloquent的attach/detach/sync会触发任何事件吗?

25

我有一个 Laravel 项目,我需要在保存模型并将一些数据附加到它之后立即进行一些计算。

在 Laravel 中调用 attach(或 detach / sync)后是否触发任何事件?


1
据我所知,没有称为“”的事件。但是您可以使用事件处理程序来触发一个。 - Daniel Luca CleanUnicorn
谢谢!@hydrarulz 是的,但我必须注意每次在那个特定模型上使用 attach 时手动触发它,不是最优选择。 - Mihai Crăiță
4个回答

28
不,Eloquent 中没有关联事件。但是你可以很容易地自己实现(以 Ticket belongsToMany Component 关系为例):
// 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));
    }
}

事件对象就是这么简单:

<?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;
    }
}

然后,我们以一个基本的监听器作为一个合理的例子:
    // 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());
    });

使用说明:

$ php artisan tinker

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

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

detached: 4
attached: 1,3
=> null

当然,你可以不创建事件对象来完成这个任务,但是使用事件对象更加方便、灵活和简单。

谢谢你的好回答!那么我需要在同步时使用我的新定义函数(例如你的示例中的syncComponents)和我的模型吗? - Mihai Crăiță
好的,事实上这正是我想避免的,要重写每个调用了attach/sync的文件。但你给了我非常好的答案,伙计! - Mihai Crăiță

12

12

解决问题的步骤:

  1. 创建自定义的BelongsToMany关系
  2. 在BelongsToMany自定义关系中覆盖attach、detach、sync和updateExistingPivot方法
  3. 在覆盖的方法中分派所需的事件。
  4. 在模型中重写belongsToMany()方法,返回自定义关系而不是默认关系

就是这样。我创建了一个已经完成上述步骤的包:https://github.com/fico7489/laravel-pivot


5

更新:

从 Laravel 5.8 开始,Pivot 模型事件会像普通模型一样被分派。

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

你只需要在关联中添加 using(PivotModel::class),事件就会在PivotModel上工作。 Attach($id) 将触发 Created 和 Creating 事件。 Detach($id) 将触发 Deleting 和 Deleted 事件, Sync($ids) 也将触发所需的事件 [Created,Creating,Deleting,Deleted]。
直到现在,只有不带ID的 dispatch() 不会触发任何事件。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接