bart's avatar
Level 13

Pass data from listener to event

Hey everybody,

I'm using events on my current project and like to pass data from one listener to another by pushing it to the event. But it won't work. Maybe because the listeners are queued. Do you have any idea? Here I go with some code:

// Event service provider
protected $listen = [
        'App\Events\HasBeenUpdated' => [
            'App\Listeners\Action1',
            'App\Listeners\Action2',
        ],
    ];

// HasBeenUpdated.php
public $rejects;

public function __construct()
{
        $this->rejects = 0;
}

// Action1.php
public function handle(HasBeenUpdated $event)
{
        $event->rejects = 1;
}

// Action2.php
public function handle(HasBeenUpdated $event)
{
        dd($event->rejects); // Result is 0 but I expected it to be 1
}

Thanks for ya help!

0 likes
7 replies
martinbean's avatar

@bart That’s not how events/listeners work. You’re supposed to pass data from the event to the listener, not the other way around. Listeners listen for events. It’s does make sense to then have communication to flow the other way.

Instead, raise other events from your listener if you need to, but having more events you have raised by events is going to make your code harder to debug in the future.

bart's avatar
Level 13

Well I thought about it but in my case it does make sense. I do different steps (different listeners for one event) and collect rejects during this process. In the end I want to send one email with collected information about the rejects. That is not possible if I would fire a single event from each listener.

belisar's avatar

It will not do what you expect because the App\Events\HasBeenUpdated instance is the same in both cases and it is not a singleton (neither should it be). The event is fired with a rejects property set at 0. This instance of it has it set at zero. Both listeners receive it. One of them makes a change to the property but the other one does not really care. It shouldn't care, the instance it receives is the unmodified one.

The most important thing however is that events are meant as simple data transfer objects and I would not alter the event itself after it has been fired. Think of their instances as write once, read many times kind of thing.

So the reject property should be given to the event when it is called, not set internally with the intention to be changed afterwards. Even if you fire the event itself again within the first event, while I am note sure, it will create a new instance internally and set the rejects to 0 again as specified in the constructor.

If you can say what exactly are you trying to achieve, maybe I can suggest a concrete solution.

bart's avatar
Level 13

Hey @belisar and thanks a lot for your detailed explanation. As I wrote above I'm trying to manipulate data from listener to listener and want to remember something I called "rejects". In the and (in the last listener) I like to send one email that contains all rejects from all listeners.

belisar's avatar
belisar
Best Answer
Level 14

One solution is to fire the first event a give it the reject value.

// HasBeenUpdated.php
public $rejects;

public function __construct($rejects)
{
        $this->rejects = $rejects;
}

fire as event(new HasBeenUpdated($rejects)

You can then listen only once and from there fire subsequent jobs.

// Action1.php (Listener)
public function handle(HasBeenUpdated $event)
{
        // pass the rejects value not the event.
        dispatch(new SecondAction($event->rejects++)
}

// SecondAction.php (Job)
public $rejects;

public function __construct($rejects)
{
    $this->rejects = $rejects;
}
public function handle($rejects)
{
        dispatch(new ThirdAction($rejects++)
}

So listen once, and pass the actual data rejects not the event itself to subsequent jobs, each of them can fire other jobs that take in the rejects action. However, for uses like this I would look into the Pipeline pattern, either using Laravel's own implementation:

http://culttt.com/2015/09/28/how-to-use-the-pipeline-design-pattern-in-laravel/

or checking out the League\Pipeline package.

http://pipeline.thephpleague.com/

bart's avatar
Level 13

Well, thank you so much @belisar. That will solve the problem imho. I will give it a further read but I think that's exactly what I've looked for. I'm a bit confused, that I never heard of the pipeline design pattern. But hey, we never stop learning.

martinbean's avatar

@bart I don’t really think you have a grasp of events and listeners.

What you’re trying to do is change the state on an entity. Events shouldn’t be manipulating state. An event is saying something happened in your application, and a listener reacts to that event happening.

It’s difficult to suggest a better alternative to your problem as you’re just giving us “action” as “reject” as names. What’s an “action”? What is being accepted/rejected?

Please or to participate in this conversation.