2014-10-07 22 views
5

我困在一个奇怪的问题上。感觉就像在Laravel一样,你不允许多个模型观察者倾听同一个事件。在我的情况: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; 
     } 
    } 
} 

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

$实例= MyChild ::创建();

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

但是,如果我在 MyChild :: creating()之后移动子代内的parent :: boot(),它确实可行。 (我没有确认doSomethingInParent()是否触发,但我假设它没有)

Can Laravel是否有多个注册到Model :: creating()的事件?

回答

15

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

首先,我会假设你想键入MyParent(不myParent),那你的意思是你的boot方法是protected,而不是private,那你包括在你create方法调用最终)。否则,你的代码不会运行。 :)

但是,您描述的问题是真实的。其原因是肯定的雄辩事件被认为是“停止”事件。也就是说,对于某些事件,如果从事件处理程序返回任何非空值(无论是闭包还是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和回调返回任何,这不是空(truefalse,一个sclaer值,array,一个object),该fire方法短路与return $response,事件停止传播。这超出了标准“返回false停止事件传播”。有些事件已经停止了。

那么,哪些模型事件会停止?如果你看一下在基础雄辩模型类fireModelEvent定义(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也将取消他们正在侦听的动作。

删除返回值(或return null),你会很开心。

+0

AlanStorm不错! – 2014-10-08 10:02:49

+0

太棒了!我想如果我觉得足够努力,这是有道理的。如果一个听众取消创作,其他听众不应该开火。我真的很惊讶,返回“真实”也停止。 Anywho,伟大的解释AlanStorm - 谢谢! – Anthony 2014-10-09 13:54:27

+0

@Anthony这有点奇怪/不明显,但这里并不容易赢。这就是为什么像Drupal这样的系统有事件的事件系统,以及需要操作数据的事件的“钩子”系统。 – 2014-10-09 16:50:50