我遇到了一个奇怪的问题。感觉就像在 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() 之后将 child::boot() 移动到子级中,那么它确实可以工作。 (我没有确认 doSomethingInParent() 是否会触发,但我假设它不会)
Laravel 可以向 Model::creating() 注册多个事件吗?
最佳答案
这个很棘手。简短版本:从处理程序中删除返回值,两个事件都会触发。长版本如下。
首先,我假设您要输入 MyParent
(不是 myParent
),您的意思是您的 boot
方法为 protected
,而不是private
,并且您添加了最终的 )
在你的create
方法调用。否则你的代码将无法运行。 :)
但是,您描述的问题是真实存在的。原因是某些 Eloquent 事件被视为“停止”事件。也就是说,对于某些事件,如果从事件处理程序返回任何非空值(无论是闭包还是 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
并且回调返回不为 null 的任何内容( true
、 false
、标量值、 array
、 object
)、 fire
方法短路 return $response
,事件停止传播。这超出了标准“返回 false
以停止事件传播”。有些事件内置了停止功能。
那么,哪些 Model 事件停止了?如果你看一下 fireModelEvent
的定义在 Eloquent 模型基类中(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 来停止事件传播的行为不同。对于这四个模型事件,返回 false
还将取消他们正在监听的操作。
删除返回值(或 return null
),你就可以开始了。
关于laravel - Laravel 中多个模型观察者的问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26244817/