Laravel Events & Listeners Reference

A Laravel events and listeners reference: dispatching events, listener discovery, queued listeners, model events, and observers.

What events and listeners are for

An event is just a statement that something happened — OrderShipped, UserRegistered — with no opinion on what should be done about it. A listener is the opposite: it reacts to an event without needing to know where the event came from. Splitting the two apart keeps the code that triggers something (a controller, a job, another listener) decoupled from an open-ended, growing list of side effects — sending an email, updating a stat, notifying a Slack channel — without that triggering code having to know or care about any of them.

Creating an event and listener

php artisan make:event OrderShipped
php artisan make:listener SendShipmentNotification
class OrderShipped
{
    use Dispatchable, SerializesModels;

    public function __construct(
        public Order $order,
    ) {}
}
class SendShipmentNotification
{
    public function handle(OrderShipped $event): void
    {
        $event->order->customer->notify(new ShipmentSent($event->order));
    }
}

Dispatching an event

OrderShipped::dispatch($order);

// or, equivalently
event(new OrderShipped($order));

Listener discovery

Current versions of Laravel find listeners automatically by scanning the app/Listeners directory and matching each listener's handle() method to the event type it's type-hinted against — there's no separate EventServiceProvider map to keep updated for most cases. A listener only needs registering by hand for cases discovery can't infer, such as a closure-based listener or one that lives outside the conventional location.

Queued listeners

A listener can do slow work — sending mail, calling an API — without slowing down whatever dispatched the event, by implementing ShouldQueue just like a job:

class SendShipmentNotification implements ShouldQueue
{
    public function handle(OrderShipped $event): void
    {
        $event->order->customer->notify(new ShipmentSent($event->order));
    }
}

This is the main point where the two topics meet: once a listener implements ShouldQueue, it behaves exactly like any other queued job — same connection, same worker, same retry and backoff options. See the queues guide for how dispatching, workers, and failures work in full.

Model events and observers

Eloquent models fire their own events through their lifecycle — creating, created, updating, updated, saving, deleting, deleted, and more — which can be hooked directly:

protected static function booted(): void
{
    static::created(function (Post $post) {
        Cache::forget('posts.count');
    });
}

Once there's more than one or two of these, an Observer keeps them out of the model class itself:

php artisan make:observer PostObserver --model=Post
class PostObserver
{
    public function created(Post $post): void
    {
        Cache::forget('posts.count');
    }

    public function deleting(Post $post): void
    {
        $post->comments()->delete();
    }
}

See the Eloquent guide for more on models and their lifecycle.

Framework-built-in events

Laravel dispatches its own events for things happening under the hood, which can be listened to the same way as an app-defined one — for example Illuminate\Auth\Events\Login fires whenever a user successfully authenticates, useful for recording a last-login timestamp or an audit log entry without touching the authentication code itself.

Notifications from a listener

Sending a notification is one of the most common things a listener does — reacting to "this happened" by alerting someone about it. See the mail and notifications guide for building the notification itself.

Testing with Event::fake()

Event::fake();

// ... code under test that should dispatch OrderShipped ...

Event::assertDispatched(OrderShipped::class, function ($event) use ($order) {
    return $event->order->id === $order->id;
});

Event::assertNotDispatched(OrderCancelled::class);

Event::fake() prevents listeners from actually running during the test, so the assertion checks that the right event was dispatched with the right data without triggering its real side effects — the same idea as Mail::fake() and Notification::fake() on the testing page.