【问题标题】:Trouble with multiple model observers in LaravelLaravel 中多个模型观察者的问题
【发布时间】:2014-10-07 20:35:42
【问题描述】:

我遇到了一个奇怪的问题。感觉就像在 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(),确实如此。

如果我将 parent::boot() 移动到子 after MyChild::creating() 中,它确实有效。 (我没有确认 doSomethingInParent() 是否会触发,但我认为它不会触发)

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

【问题讨论】:

    标签: laravel eloquent


    【解决方案1】:

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

    首先,我假设您的意思是输入MyParent(不是myParent),您的意思是您的boot 方法是protected,而不是private,并且您包含了一个在您的 create 方法调用中使用最终的 )。否则你的代码不会运行。 :)

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

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

    如果 halt 是 true 并且回调返回 anything 不为空(truefalse、sclaer 值、arrayobject),则 @ 987654336@ 方法与return $response 短路,事件停止传播。这超出了标准“返回false 以停止事件传播”。某些事件已停止内置。

    那么,哪些模型事件会停止?如果您查看基本 eloquent 模型类中 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,都会暂停该事件并且您的第二个侦听器没有触发。当模型类想要对事件的返回值做某事时,通常会使用停止事件。专门针对creating

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

    如果你从回调中返回false,(非空),Laravel 实际上会跳过执行INSERT。同样,这与标准通过返回 false 来停止事件传播的行为不同。对于这四个模型事件,返回 false 也会取消他们正在监听的动作。

    删除返回值(或return null),一切顺利。

    【讨论】:

    • 太棒了!我想如果我足够努力地思考,那是有道理的。如果一个侦听器取消创建,则其他侦听器不应触发。我很惊讶返回“true”也停止了。任何人,很好的解释 AlanStorm - 谢谢!
    • @Anthony 这有点奇怪/不明显,但这里没有轻松的胜利。这就是为什么像 Drupal 这样的系统有用于事件的事件系统,以及用于需要操作数据的事件的“挂钩”系统。
    • 这是一个巨大的惊喜,而且它的工作方式如此晦涩难懂,应该被认为是不好的使用习惯。在 Laravel 文档“取消保存操作”中曾经有一些关于此的内容,但现在即使这样也没有了。您会注意到“可暂停”事件都以 -ing... 结尾,因此如果您想停止正在处理的正在处理的内容,只需抛出异常即可。免费福利:如果确实发生,也不会被忽视。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多