Listeners and Events in Laravel

Video thumbnail

Listeners and events in Laravel are one of those tools that, when you understand them well, completely change the way you structure an application. Not only do they make the code cleaner, but they also allow you to decouple logic, reuse behaviors, and avoid controllers bloated with responsibilities.

In this article, I'm going to explain what they are, how they really work, what changed from Laravel 11 onwards, and most importantly: when it makes sense to use them, with practical examples and a real-life case I encountered while working with authentication and sessions.

What are events and listeners in Laravel?

Laravel natively implements the observer pattern, which means that one part of the system can "notify" that something happened, and other parts can react to it without being directly coupled.

Event: what it represents and when it's fired

An event is simply a notification that something happened in the application.
Typical examples:

  • A user registered
  • A user logged in
  • An order was created
  • An important model was updated

The event contains no logic, only data related to what happened.

Listener: what it does and why it's key

A listener is the class that reacts to the event and executes a specific action:

  • Send an email
  • Send a notification
  • Update stock
  • Log information
  • Synchronize data

The key is that the event doesn't know who is listening, and the listener doesn't need to know where the event was fired.

Observer Pattern Applied in Laravel

Thanks to this pattern:

  • The code remains decoupled
  • You can add or remove behaviors without touching the main logic
  • A single event can have many listeners

This is exactly what Laravel does internally with authentication, registration, email verification, etc.

What are event listeners for in Laravel?

Listeners are used to move secondary logic out of the main application flow.

Decoupling business logic

Instead of doing this in a controller:

 
// save user // send email // notify admin // write to logs 

You can simply:

 
event(new UserRegistered($user));

And let each listener do its job.

Executing multiple actions from a single event

An event can trigger:

  • A listener that sends an email
  • Another that notifies the administrator
  • Another that updates metrics

All independent of each other.

Typical use cases

  • Welcome emails
  • Notifications (Slack, SMS, push)
  • Logging
  • Background processes
  • Data synchronization
  • Internal automations

How events and listeners work in Laravel (full flow)

The flow is always the same:

1. Firing an event

When something important happens:

 
event(new UserRegistered($user));
// or UserRegistered::dispatch($user);

2. Listening for the event

Laravel automatically looks for listeners associated with that event.

3. Executing the listener

The method is executed:

 
handle() 
// or __invoke()

of the corresponding listener.

Important changes since Laravel 11: listener auto-discovery

Here is a key point that many people overlook.

How it worked before (EventServiceProvider)

Before Laravel 11, you had to manually register everything in EventServiceProvider:

 
protected $listen = [
    UserRegistered::class => [
        SendWelcomeEmail::class,
    ],
];

How auto-discovery works now

Since Laravel 11, this is no longer mandatory.

Laravel automatically scans the app/Listeners directory and:

  • Detects handle() methods
  • Or __invoke() methods
  • Uses the event's type-hint to associate them

In most cases, you don't need to touch the EventServiceProvider.

Which methods Laravel detects

  • public function handle(Event $event)
  • public function __invoke(Event $event)

Simple, clean, and without extra configuration.

Creating an event and its listener in Laravel (basic example)

Event Definition

 
namespace App\Events;
use App\Models\User;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Queue\SerializesModels;
class UserRegistered
{
    use Dispatchable, SerializesModels;
    public function __construct(
        public readonly User $user
    ) {}
}

The event only carries data.

Listener Creation

 
php artisan make:listener SendWelcomeEmail --event=UserRegistered
 
namespace App\Listeners;
use App\Events\UserRegistered;
class SendWelcomeEmail
{
    public function handle(UserRegistered $event): void
    {
        // Mail::to($event->user)->send(...)
    }
}

Laravel detects this automatically.

Dispatching the event

 
event(new UserRegistered($user));

Or:

 
UserRegistered::dispatch($user);

Real-world case: using a listener on Laravel's login

This is where listeners truly shine.

In my case, I had this scenario:

  • A user adds products to the cart before authenticating
  • That data is saved in the database
  • Upon login, I needed to replicate the cart from the DB to the session

I could have:

  • Overridden the login method
  • Put logic in the controller
  • Touched Fortify

But I preferred something much more modular.

The problem: synchronizing data after authentication

Laravel already fires the event internally:

 
Illuminate\Auth\Events\Login

So it made no sense to reinvent anything.

Why use a listener instead of overriding the login

  • I don't couple business logic to the authentication process
  • I can change or remove the behavior without touching the login
  • The code remains reusable and clear

Listening to Laravel's Login event

 
php artisan make:listener LoginSuccessful --event=Illuminate\Auth\Events\Login
 
namespace App\Listeners;
use Illuminate\Auth\Events\Login;
use App\Models\ShoppingCart;
use Illuminate\Support\Arr;
class LoginSuccessful
{
    public function handle(Login $event): void
    {
        $cartDB = ShoppingCart::where('user_id', auth()->id())->get();
        $cartSession = session('cart', []);
        foreach ($cartDB as $c) {
            if (Arr::exists($cartSession, $c->post->id)) {
                $cartSession[$c->post->id][1] = $c->count;
            } else {
                $cartSession[$c->post->id] = [$c->post, $c->count];
            }
        }
        session(['cart' => $cartSession]);
    }
}

Every time the user logs in, the cart is automatically synchronized.
Without touching the login. No hacks.

Listener vs direct callback: when to use each

Laravel also allows listening to events directly with a callback.

Dedicated Listener

✔ Cleaner
✔ Better scalability
✔ Ideal for reusable logic

Callback in AppServiceProvider

✔ Faster for simple scripts
✔ Fewer files

 
Event::listen(Login::class, function () {
    // quick logic
});

For small things it's fine, but when it grows, the dedicated listener wins.

Queued Listeners in Laravel

If the listener does something slow (emails, HTTP, integrations), it should not run in the main request.

When to use queued listeners

  • Sending emails
  • External notifications
  • Heavy processes

Implementing ShouldQueue

 
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Queue\InteractsWithQueue;
class SendWelcomeEmail implements ShouldQueue
{
    use InteractsWithQueue;
    public function handle(UserRegistered $event): void
    {
        //
    }
}

This improves performance and user experience.

Events, queues, and database transactions

A common mistake: using queued listeners before a transaction commits.

The problem

The listener can run when the data does not yet exist in the DB.

Solution: ShouldHandleEventsAfterCommit

 
use Illuminate\Contracts\Events\ShouldHandleEventsAfterCommit;
class SendWelcomeEmail implements ShouldQueue, ShouldHandleEventsAfterCommit
{
    //
}

This way you ensure total consistency.

Common mistakes when using events and listeners in Laravel

  • Putting too much logic inside the listener
  • Using events for trivial things
  • Not considering queues
  • Ignoring transactions
  • Creating unnecessary events

Events are a tool, not a religion.

Frequently asked questions about events and listeners in Laravel

Is it mandatory to use EventServiceProvider in Laravel 11?
No, except for advanced configurations.

Can I use events without queues?
Yes, they are synchronous by default.

Listeners or direct logic in the controller?
If the logic is repeated or grows, use a listener.

What event is fired on login?
Illuminate\Auth\Events\Login

Conclusion

Listeners and events in Laravel are one of the best ways to maintain a clean, scalable, and easy-to-maintain architecture. With Laravel 11, their use is even simpler thanks to auto-discovery, and when you combine them with queues and good practices, they become an extremely powerful tool.

In my experience, using them to solve real problems (like synchronizing data after login) makes the difference between a quick fix and a well-designed solution.

Learn what events and listeners are in Laravel, how they work in Laravel, and when to use them with real-world examples, queues, and best practices.

I agree to receive announcements of interest about this Blog.

Andrés Cruz

ES En español