laravel - Laravel 中多个模型观察者的问题

标签 laravel eloquent

我遇到了一个奇怪的问题。感觉就像在 Laravel 中,不允许多个模型观察者监听同一个事件。就我而言:

父模型

class MyParent extends Eloquent {
   private static function boot()
   {
      parent::boot();
      $called_class = get_called_class();
      $called_class::creating(function($model) {
         doSomethingInParent();
         return true;
      }
   }
}

子模型

class MyChild extends myParent {
   private static function boot()
   {
      parent::boot();
      MyChild::creating(function($model) {
         doSomethingInChild();
         return true;
      }
   }
}

在上面的例子中,如果我这样做:

$instance = MyChild::create();

... doSomethingInChild() 行不会触发。 doSomethingInParent() 确实如此。

如果我在 MyChild::creating() 之后将 child::boot() 移动到子级中,那么它确实可以工作。 (我没有确认 doSomethingInParent() 是否会触发,但我假设它不会)

Laravel 可以向 Model::creating() 注册多个事件吗?

最佳答案

这个很棘手。简短版本:从处理程序中删除返回值,两个事件都会触发。长版本如下。

首先,我假设您要输入 MyParent (不是 myParent ),您的意思是您的 boot方法为 protected ,而不是private ,并且您添加了最终的 )在你的create方法调用。否则你的代码将无法运行。 :)

但是,您描述的问题是真实存在的。原因是某些 Eloquent 事件被视为“停止”事件。也就是说,对于某些事件,如果从事件处理程序返回任何非空值(无论是闭包还是 PHP 回调),事件将停止传播。您可以在调度程序中看到这一点

#File: vendor/laravel/framework/src/Illuminate/Events/Dispatcher.php
public function fire($event, $payload = array(), $halt = false)
{
}

查看第三个参数$halt ?稍后,当调度程序调用事件监听器时

#File: vendor/laravel/framework/src/Illuminate/Events/Dispatcher.php
    foreach ($this->getListeners($event) as $listener)
    {
        $response = call_user_func_array($listener, $payload);

        // If a response is returned from the listener and event halting is enabled
        // we will just return this response, and not call the rest of the event
        // listeners. Otherwise we will add the response on the response list.
        if ( ! is_null($response) && $halt)
        {
            array_pop($this->firing);

            return $response;
        }

    //...

如果停止是 true并且回调返回不为 null 的任何内容( truefalse 、标量值、 arrayobject )、 fire方法短路 return $response ,事件停止传播。这超出了标准“返回 false 以停止事件传播”。有些事件内置了停止功能。

那么,哪些 Model 事件停止了?如果你看一下 fireModelEvent 的定义在 Eloquent 模型基类中(Laravel 将其别名为 Eloquent )

#File: vendor/laravel/framework/src/Illuminate/Database/Eloquent/Model.php
protected function fireModelEvent($event, $halt = true)
{
    //...
}

您可以看到模型的事件默认停止。因此,如果我们查看触发事件的模型,我们会看到停止的事件是

#File: vendor/laravel/framework/src/Illuminate/Database/Eloquent/Model.php
$this->fireModelEvent('deleting') 
$this->fireModelEvent('saving')
$this->fireModelEvent('updating')
$this->fireModelEvent('creating')

不停止的事件是

#File: vendor/laravel/framework/src/Illuminate/Database/Eloquent/Model.php
$this->fireModelEvent('booting', false);
$this->fireModelEvent('booted', false);
$this->fireModelEvent('deleted', false);
$this->fireModelEvent('saved', false);
$this->fireModelEvent('updated', false);
$this->fireModelEvent('created', false);

如您所见,creating是一个停止事件,这就是为什么返回任何值,甚至 true ,停止了事件并且你的第二个监听器没有触发。当 Model 类想要对事件的返回值执行某些操作时,通常会使用停止事件。专为creating

#File: vendor/laravel/framework/src/Illuminate/Database/Eloquent/Model.php
protected function performInsert(Builder $query)
{
    if ($this->fireModelEvent('creating') === false) return false;
    //...

如果你返回false , (not null) 从你的回调中,Laravel 实际上会跳过执行 INSERT 。同样,这与标准通过返回 false 来停止事件传播的行为不同。对于这四个模型事件,返回 false还将取消他们正在监听的操作。

删除返回值(或 return null ),你就可以开始了。

关于laravel - Laravel 中多个模型观察者的问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26244817/

相关文章:

php - 我如何将日期时间分成不同的部分?

php - 未通过中间件时来自节流请求的 TypeError

Laravel 多对多关系 - 如果没有其他附加关系,则全部分离并删除

Laravel/Eloquent 保存父/子关系验证

sql - Laravel/ Eloquent : querying a belongsTo/hasMany relation

laravel 5.5 不支持 jwt 授权库

php - 使用 ajax 通过 Laravel Controller 传递数据

php - 使用内置的 Laravel 5.2 身份验证并加载 SPA,然后为所有其他路由加载 Dingo API

mysql - 无法在 store 方法中传入数据

php - OrderBy Coupons 基于过期状态 Laravel