2014-10-07 6 views
5

Ich stecke auf einem seltsamen Problem fest. Es fühlt sich an wie in Laravel, dass es nicht erlaubt ist, dass mehrere Modellbeobachter dasselbe Ereignis hören. In meinem Fall:Probleme mit mehreren Modellbeobachtern in Laravel

Eltern Modell

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

Child Modell

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

In dem obigen Beispiel, wenn ich tun:

$ instance = MyChild :: erstellen();

... die Zeile doSomethingInChild() wird nicht ausgelöst. doSomethingInParent(), tut.

Wenn ich übergeordnete :: boot() innerhalb des Kindes nach MyChild :: creating(), aber es funktioniert. (Ich habe nicht bestätigt, ob doSomethingInParent() ausgelöst wird, aber ich nehme an, dass dies nicht der Fall ist)

Kann Laravel mehrere Ereignisse in Model :: creating() registriert haben?

Antwort

15

Dieser ist knifflig. Kurze Version: Entfernen Sie Ihre Rückgabewerte von Ihren Handlern und beide Ereignisse werden ausgelöst. Lange Version folgt.

Erstens, ich werde Sie übernehmen soll MyParent (nicht myParent) geben, dass Sie Ihre boot Methoden gemeint protected zu sein, und nicht private, und dass Sie eine endgültige ) in Ihrem create Methodenaufrufe enthalten. Andernfalls wird der Code nicht ausgeführt. :)

Allerdings ist das Problem, das Sie beschreiben, real. Der Grund dafür ist, dass beredte Ereignisse als "haltende" Ereignisse gelten. Das heißt, wenn bei einigen Ereignissen irgendein Nicht-Null-Wert von den Event-Handlern zurückgegeben wird (sei es ein Closure- oder PHP-Callback), wird das Ereignis nicht weiter verbreitet. Sie können dies in den

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

Dispatcher sehen $halt diesen dritten Parameter sehen? Später, während der Dispatcher Ereignis-Listener ist

#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; 
     } 

    //... 

Wenn halt true und der Rückruf zurückgegeben ruft etwas, die nicht null ist (true, false, ein sclaer Wert, ein array, ein object), die fire Verfahren Kurzschlüsse mit einem return $response, und die Ereignisse stoppen sich zu verbreiten. Dies ist über diesen Standard hinaus "false zurückgeben, um die Ereignisausbreitung zu stoppen". Einige Ereignisse haben Halt eingebaut.

Also, welche Model Events anhalten? Wenn Sie bei der Definition von fireModelEvent in der Basis eloquent Modellklasse aussehen (Laravel Aliase dies als Eloquent)

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

können Sie ein Modell der Ereignisse Standard zu Anhalten sehen.Also, wenn wir das Modell aussehen durch Veranstaltungen zum Brennen, sehen wir die Ereignisse, die tun halt sind

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

und Ereignisse, die

#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); 

nicht aufhalten werden Sie sehen, creating ist ein Halteereignis, weshalb das Zurückgeben eines Werts, auch true, das Ereignis angehalten hat und Ihr zweiter Listener nicht ausgelöst wurde. Halteereignisse werden normalerweise verwendet, wenn die Model-Klasse etwas mit dem Rückgabewert eines Ereignisses ausführen möchte. Speziell für creating

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

wenn Sie false zurückkehren, (nicht null) von Ihrem Rückruf, Laravel wird überspringen tatsächlich die INSERT durchführen. Dies ist wiederum ein anderes Verhalten als bei der standardmäßigen Stop-Ereignisausbreitung durch Rückgabe von false. Bei diesen vier Modellereignissen wird durch die Rückgabe von false auch die Aktion abgebrochen, auf die sie warten.

Entfernen Sie die Rückgabewerte (oder return null) und Sie werden gut gehen.

+0

AlanStorm netter! –

+0

Brilliant! Ich nehme an, wenn ich genug nachdenke, macht es Sinn. Wenn ein Listener die Erstellung abbricht, sollten die anderen Listener nicht auslösen. Ich war nur überrascht, dass die Rückkehr "wahr" auch stockte. Anywho, tolle Erklärung AlanStorm - danke! – Anthony

+0

@Anthony Es ist ein bisschen komisch/nicht offensichtlich, aber es gibt keinen leichten Sieg hier. Aus diesem Grund haben Systeme wie Drupal ein Ereignissystem für Ereignisse und ein "Hooks" -System für Ereignisse, die Daten manipulieren müssen. –

Verwandte Themen