aslain.dev
0%
01 Hizmetler 02 Hakkımda 03 Projeler 04 Stack 05 Blog 06 İletişim
← Tüm makaleler Webontwikkeling

Laravel Events en Listeners: gids voor losgekoppelde code

Een Laravel event is de schoonste manier om aan te kondigen dat er "iets is gebeurd" in je applicatie, terwijl je de code die erop reageert volledig gescheiden houdt. In plaats van drie verschillende taken — een welkomstmail sturen, een logregel schrijven en een statistiek bijwerken — in één controller te proppen wanneer een gebruiker zich registreert, publiceer je één event en delen de listeners die zich erop abonneren het werk. Het resultaat is een losgekoppelde, testbare codebasis die openstaat voor groei. In dit artikel bouwen we de event/listener-architectuur vanaf nul op, zetten we hem in de wachtrij en zien we stap voor stap hoe je hem in echte scenario's gebruikt.

Wat is het idee achter event en listener?

Het idee is eenvoudig: een event is een berichtobject dat "iets dat is gebeurd" voorstelt — bijvoorbeeld OrderShipped. Een listener is de code die op dat event reageert — zoals de klant mailen. De code die het event publiceert hoeft niet te weten wie er luistert. Dit is het klassieke publish/subscribe-patroon, en het brengt een paar belangrijke voordelen:

  • Losse koppeling: de controller weet niet of er een mail is verstuurd of een log is geschreven. Om een nieuwe reactie toe te voegen schrijf je simpelweg een nieuwe listener.
  • Enkele verantwoordelijkheid: elke listener doet één ding, dus klassen blijven klein en leesbaar.
  • Makkelijk testen: je kunt in één regel bevestigen dat het event is gedispatcht.

Events en listeners aanmaken

Je kunt beide genereren met artisan. Bij het aanmaken van de listener vertelt de --event-vlag welke event hij moet beluisteren, wat je wat bekabeling bespaart:

php artisan make:event OrderShipped
php artisan make:listener SendShipmentNotification --event=OrderShipped

Een event-klasse draagt meestal alleen data. Het betreffende model aan de constructor doorgeven is genoeg:

<?php

namespace App\Events;

use App\Models\Order;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Queue\SerializesModels;

class OrderShipped
{
    use Dispatchable, SerializesModels;

    public function __construct(public Order $order) {}
}

De listener doet het echte werk in zijn handle()-methode. De parameter die je type-hint bepaalt naar welk event hij luistert:

<?php

namespace App\Listeners;

use App\Events\OrderShipped;
use App\Notifications\ShipmentNotification;

class SendShipmentNotification
{
    public function handle(OrderShipped $event): void
    {
        $event->order->user->notify(
            new ShipmentNotification($event->order)
        );
    }
}

Listeners registreren

In Laravel 11 en 12 worden listeners automatisch ontdekt. Wanneer een klasse in de map app/Listeners een event type-hint in zijn handle()-methode, legt het framework de koppeling voor je; er is geen extra registratie nodig. Dit maakt de $listen-array in de oude EventServiceProvider overbodig.

Er zijn nog steeds gevallen waarin handmatige registratie handig is — bijvoorbeeld een snelle listener op basis van een closure. Je kunt er een registreren in de boot()-methode van AppServiceProvider:

use App\Events\OrderShipped;
use Illuminate\Support\Facades\Event;

public function boot(): void
{
    Event::listen(function (OrderShipped $event) {
        // een snelle, eenmalige reactie
    });
}

Het event dispatchen

Om een event te publiceren gebruik je de statische methode dispatch of de globale event()-helper. Beide doen hetzelfde:

use App\Events\OrderShipped;

// statische methode
OrderShipped::dispatch($order);

// of de event()-helper
event(new OrderShipped($order));

Op het moment dat deze regel draait, wordt elke listener die zich op OrderShipped abonneert één voor één geactiveerd. Je controller publiceert alleen het event; de rest is aan de listeners. Een typisch gebruik ziet er zo uit:

public function ship(Order $order)
{
    $order->markAsShipped();

    OrderShipped::dispatch($order);

    return back()->with('status', 'Bestelling verzonden.');
}

Listeners in de wachtrij zetten

Traag werk zoals het versturen van e-mail mag het verzoek niet ophouden. Om een listener naar de achtergrond te verplaatsen hoef je alleen de ShouldQueue-interface te implementeren:

<?php

namespace App\Listeners;

use App\Events\OrderShipped;
use Illuminate\Contracts\Queue\ShouldQueue;

class SendShipmentNotification implements ShouldQueue
{
    public int $tries = 3;
    public int $backoff = 30;

    public function handle(OrderShipped $event): void
    {
        // draait in de achtergrond
    }
}

De listener wordt nu op een wachtrij gezet en door een worker verwerkt. $tries bepaalt het aantal nieuwe pogingen en $backoff de wachttijd (in seconden) ertussen. Om de wachtrij te laten draaien moet je een worker actief houden in de achtergrond:

php artisan queue:work

Je kunt ook een failed()-methode toevoegen die afgaat wanneer de listener definitief mislukt, waar je een beheerder kunt waarschuwen of een log kunt schrijven. Een belangrijk detail: listeners in de wachtrij serialiseren het event-object, dus dankzij de SerializesModels-trait wordt alleen de sleutel van het model opgeslagen en wordt het model vers opnieuw uit de database geladen wanneer de job draait.

Listeners groeperen met een event subscriber

Als je meerdere listeners voor één onderwerp in één klasse wilt verzamelen, kun je een subscriber gebruiken. Een subscriber is een klasse die zijn eigen event-toewijzingen registreert via een subscribe()-methode:

<?php

namespace App\Listeners;

use Illuminate\Events\Dispatcher;

class OrderEventSubscriber
{
    public function handleShipped($event): void { /* ... */ }
    public function handleCancelled($event): void { /* ... */ }

    public function subscribe(Dispatcher $events): array
    {
        return [
            \App\Events\OrderShipped::class => 'handleShipped',
            \App\Events\OrderCancelled::class => 'handleCancelled',
        ];
    }
}

Deze aanpak is ideaal om gerelateerde events, zoals de levenscyclus van een bestelling, op één leesbare plek te houden.

Veelgestelde vragen

Moet ik een event/listener of een job gebruiken?

De twee vullen elkaar aan. Een job zegt "doe deze taak in de achtergrond"; het is één specifieke taak. Een event zegt "dit is gebeurd" en kan meerdere onafhankelijke reacties activeren. Als er maar één taak is, dispatch dan direct een job; als meerdere partijen op één gebeurtenis moeten reageren, gebruik dan een event/listener (en zet de listener in de wachtrij om ook het voordeel van de job te krijgen).

Hoe bevestig ik in een test dat een event is gedispatcht?

Je faket events met Event::fake() en verifieert daarna met Event::assertDispatched(OrderShipped::class). Zo test je alleen dat het event afging, zonder de echte listeners te draaien.

Hoe verhoudt dit zich tot model events?

Eloquent vuurt automatisch zijn eigen model events af zoals created, updated en deleted. Daar met een observer-klasse naar luisteren is vaak schoner voor werk dat aan de levenscyclus van het model gebonden is (bijvoorbeeld een slug genereren bij het aanmaken). Voor bredere gebeurtenissen op bedrijfsniveau is het schrijven van je eigen event-klassen de betere keuze.

Wil je je code losgekoppeld maken? We kunnen de overvolle logica in je bestaande controllers samen opsplitsen in events en listeners; neem contact op om je Laravel-architectuur te vereenvoudigen.

Bu kategorideki tüm yazılar →

Devamı için