2014-10-07 19 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