blog.thms.uk

Event Listeners in Laravel 11

Up to Laravel 10, Event Listeners had to be registered by adding them to the $listen property of the EventServiceProvider.

Since Laravel 11, though, this has changed: Laravel 11 primarily relies on Event Discovery, and if you want to manually register Events, you are encouraged to do this in the AppServiceProvider:

use App\Domain\Orders\Events\PodcastProcessed;
use App\Domain\Orders\Listeners\SendPodcastNotification;
use Illuminate\Support\Facades\Event;
 
/**
 * Bootstrap any application services.
 */
public function boot(): void
{
    Event::listen(
        PodcastProcessed::class,
        SendPodcastNotification::class,
    );
}

A separate EventServiceProvider seemed to better encapsulate the event registration, but let's leave that aside for a moment.

The thing that annoys me more becomes apparent if you need to register multiple listeners with the same event: The Event::listen method can only register one listener at a time, so this gets verbose really quickly:

use App\Domain\Orders\Events\PodcastProcessing;
use App\Domain\Orders\Listeners\CompressPodcast;
use App\Domain\Orders\Listeners\UploadPodcast;
use App\Domain\Orders\Listeners\AddToSubscriberLibraries;
use Illuminate\Support\Facades\Event;
 
/**
 * Bootstrap any application services.
 */
public function boot(): void
{
    Event::listen(
        PodcastProcessing::class,
        CompressPodcast::class,
    );
    Event::listen(
        PodcastProcessing::class,
        UploadPodcast::class,
    );
    Event::listen(
        PodcastProcessing::class,
        AddToSubscriberLibraries::class,
    );
}

I've got some events that have 10+ listeners registered with them in some of my projects. Not great.

So, I've added the following to my AppServiceProvider to return to the old behaviour:

use App\Domain\Orders\Events\PodcastProcessing;
use App\Domain\Orders\Listeners\CompressPodcast;
use App\Domain\Orders\Listeners\UploadPodcast;
use App\Domain\Orders\Listeners\AddToSubscriberLibraries;
use Illuminate\Support\Facades\Event;
 
private $listen = [
    PodcastProcessing::class => [
        CompressPodcast::class,
        UploadPodcast::class,
        AddToSubscriberLibraries::class,
    ],
];

/**
 * Bootstrap any application services.
 */
public function boot(): void
{
    foreach ($this->listen as $event => $listeners) {
        foreach ($listeners as $listener) {
            Event::listen($event, $listener);
        }
    }
}

I'm not going to pretend this is a big deal, but to me this seems more convenient 🤷‍♂️.