Laravel中多个模型观察者的麻烦

Ant*_*ony 6 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;
      }
   }
}
Run Code Online (Sandbox Code Playgroud)

儿童模特

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

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

$ instance = MyChild :: create();

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

但是,如果我 MyChild :: creating()之后移动子进程中的parent :: boot(),它确实有效.(我没有确认doSomethingInParent()是否触发,但我认为它没有)

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

Ala*_*orm 18

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

首先,我假设您打算键入MyParent(而不是myParent),表示您的boot方法是protected,而不是private,并且您)create方法调用中包含了final .否则您的代码不会运行.:)

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

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

看到第三个参数$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;
        }

    //...
Run Code Online (Sandbox Code Playgroud)

如果停止true并且回调返回任何非空的(true,falsesclaer值,an array,an object),则该fire方法与a短路return $response,并且事件停止传播.这超出了标准"返回false停止事件传播".有些事件已经停止了.

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

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

您可以看到模型的事件默认为暂停.所以,如果我们通过模型对触发事件中,我们看到的事件停止的

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

和不停止的事件是

#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);
Run Code Online (Sandbox Code Playgroud)

正如你所看到的,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;
    //...
Run Code Online (Sandbox Code Playgroud)

如果你false从回调中返回(不为空),Laravel实际上会跳过执行INSERT.同样,这与标准停止事件传播的不同行为是返回false.在这四个模型事件的情况下,返回false也将取消他们正在侦听的操作.

删除返回值(或return null),你会很高兴.