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

时间:2014-10-07 20:35:42

标签: 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()之后移动子中的parent :: boot(),则它确实有效。 (我没有确认doSomethingInParent()是否会触发,但我认为它不会发生

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

1 个答案:

答案 0 :(得分:17)

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

首先,我假设您打算键入MyParent(而非myParent),表示您的boot方法是protected,而不是{ {1}},并且您在private方法调用中包含了最终)。否则您的代码不会运行。 :)

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

create

看到第三个参数#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; } //... 且回复任何非空(truetrue,sclaer值为false,一个array),object方法与fire短路,事件停止传播。这超出了标准“返回return $response以停止事件传播”。有些事件已经停止了。

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

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,停止事件并且您的第二个侦听器没有触发。当Model类想要对事件的返回值执行某些操作时,通常会使用暂停事件。特别针对true

creating

如果从回调中返回#File: vendor/laravel/framework/src/Illuminate/Database/Eloquent/Model.php protected function performInsert(Builder $query) { if ($this->fireModelEvent('creating') === false) return false; //... ,(非null),Laravel实际上会跳过执行false。同样,这与标准停止事件传播的行为不同,返回false 。对于这四个模型事件,返回INSERT也将取消他们正在侦听的操作。

删除返回值(或false),你就可以了。