【问题标题】:Pass array by reference to Laravel Event Listener通过引用 Laravel 事件监听器传递数组
【发布时间】:2019-05-28 02:28:19
【问题描述】:

我在 Laravel 应用程序中有一个数组,我想在 Laravel 监听器中进行修改。 PHP 默认按值传递数组,但是 Laravel 事件及其侦听器的工作方式我无法修改原始变量。有没有比我在下面做的更好的方法?

触发事件的模型。

模型:Event.php

namespace Vendor\Package\Models

use Vendor\Package\Events\PageNodeArrayAfter;
use Event;

class Page
{
   public function toArray()
   {
      $data = []; 

      // do something with the data. 

      Event::fire(new PageNodeToArrayAfter($data))

      // The data should be modified by a listener when I use it here.
   }
}

事件:PageNodeToArrayAfter.php

namespace Vendor\Package\Events;

class PageNodeToArrayAfter
{
    /**
     * Props to be sent to the view
     * @var array $data
     */
    protected $data = [];

    /**
     * @param array $data
     * 
     */
    public function __construct(array &$data)
    {
        $this->data = $data;
    }

    public function getData()
    {
        return $this->data;
    }
}

监听器:FlashMessagesListner.php

namespace Vendor\Package\Listeners;

class FlashMessagesListner
{
    protected $data = [];

    public function handle(PageNodeToArrayAfter $event)
    {
       $this->data = $event->getData();
       // The problem here is the $data is no logner a reference here. 
    }
}

【问题讨论】:

  • 您的用例是什么?为什么需要实现这一目标?即使您的侦听器可能尚未完成,您的主线程(执行事件的线程)仍将继续运行。
  • 我正在将一些 XML 节点转换为数组,在此过程中,我想通过独立的侦听器附加一些自定义数据,例如验证消息。
  • 我明白了。您需要自定义您的 XML 数据,那么为什么要使用侦听器呢?事件/侦听器用于通知应用程序的其他部分(或外部 API)某些事情,但在您的情况下,您希望对您将继续接触的相同数据进行操作。我知道你想解耦你的逻辑以避免用大量代码为你的函数充电。因此,您可以将该逻辑提取到一个函数、一个特征或另一个将返回更新的 XML 文件的类。
  • 我同意@HCK 的观点,即观察者模式在这里是一种错误的方法,因为您的转换可能会在听众完成他们的工作之前完成。
  • 事件和监听器用于解耦进程。如果您只需要将一些验证消息闪现到会话中,我看不出您需要如何通过引用传递数组。如果您的侦听器的逻辑对于应用程序的另一部分的过程至关重要,那么侦听器无论如何都是错误的选择。仅当触发进程不关心事件是否被任何其他进程使用时才应使用事件侦听器(processbusiness process,而不是线程) . IE。在用户注册后发送欢迎邮件(这对于该过程是完全可选的)。

标签: php laravel laravel-5 laravel-5.8


【解决方案1】:

感谢所有回复、对问题的反馈以及寻找更好方法的建议。

现在我没有使用监听器,而是尝试了 Laravel Pipeline,这是一种通过不同管道传递数据并在我的情况下过滤数据的好方法。这篇文章对理解很有帮助https://jeffochoa.me/understanding-laravel-pipelines

这是我的代码的最终版本以及我如何使用 Laravel 管道:

节点:Page.php

<?php declare(strict_types=1);

namespace Vendor\Package\Nodes;


class Page extends ReactPage
{
    public function toArray() : array
    {
        $data = parent::toArray();

        $pipes = [
           AppendFlashMessage::class,
           RemoveEmptyLayoutNode::class
        ];


        // Filter data through the pipelines. 
        $data = app(Pipeline::class)
            ->send($data)
            ->through($pipes)
            ->via('filter')
            ->then(function($data) {
                return $data;
        });

        return $data;
    }
}

管道:AppendFlashMessage.php

<?php declare(strict_types=1);

namespace Vendor\Package\Pipeline;

use Closure;

class AppendFlashMessage
{

    public function filter(array $data, Closure $next) : array
    {

        // step 1: pull the errors from session.
        $errors = [
            'type' => 'error',
            'message' => 'Invalid User Name'
        ];

        $data['messages'] = $errors;
        return $next($data);
    }
}

【讨论】:

    【解决方案2】:

    根据array 的文档,它是这么说的:

    数组赋值总是涉及值复制。使用引用运算符通过引用复制数组。

    所以把你的构造函数改成这个:

    // prepend the argument with the reference operator &
    public function __construct(array &$data) 
    

    【讨论】:

    • 这不能回答问题。如何修改 Listener 中的引用变量?我刚刚更新了我的问题,并包含了原本打算在那里的 & 运算符。
    • 我现在只是使用 setter 和 getter 来修改变量,现在我可能不需要通过引用传递它。
    • 它回答了您声明默认情况下 PHP 中的数组是通过引用传递而不是通过引用传递的部分。对我来说,首先使用侦听器并从侦听器内部修改外部变量是没有意义的,因为当您触发事件时该过程是异步的,即使侦听器已经执行了事件之后的下一行,没有完成它的工作。
    • 是的,这就是我现在的想法。我可能不需要通过引用将变量传递给侦听器,因此不需要我之前的评论。
    【解决方案3】:

    只需在事件的构造函数中使用参考号:

    /**
     * @param array $data
     * 
     */
    public function __construct(array &$data)
    {
        $this->data = &$data;
                      ^
    }
    

    虽然这提供了对问题的准确答案,但我不建议在此特定用例中使用观察者模式。

    此外,您不需要访问器 - 只需公开 data 属性并在侦听器中使用 $event-&gt;data

    【讨论】:

      【解决方案4】:

      触发的初始事件通过引用传递(因为它是一个对象)。该对象中修改后的 $data 属性可以在侦听器执行后访问。

        event($e = new PageNodeToArrayAfter($data));
        $data = $e->getData(); 
      

      无需在事件构造函数或 getter 中通过引用手动传递任何内容。

      【讨论】:

        猜你喜欢
        • 2014-09-12
        • 2011-05-24
        • 2016-08-13
        • 2015-10-01
        • 1970-01-01
        • 1970-01-01
        • 2020-11-29
        • 2016-01-17
        • 1970-01-01
        相关资源
        最近更新 更多