# Events and Listeners in Laravel

> How I use Laravel's event system to decouple side effects from the main flow — with practical examples of what you gain.

- Published: 2016-05-01
- Category: Framework & Library
- Tags: Laravel
- Reading time: 4 min read
- Source: https://www.muhammetsafak.com.tr/en/blog/events-and-listeners-in-laravel/
- Language: en-US
- Author: Muhammet Şafak

---
What should happen when a user registers? Send a welcome email, log the activity, maybe notify a marketing service. Writing all of that inside `UserController@register` is easy at first — but a few months later that method becomes a bloated mess.

Laravel's event and listener system exists precisely to separate these side effects from the registration action itself. When I wrote about [not bloating your controllers](/en/blog/understanding-mvc-stop-bloating-your-controllers/) I discussed where responsibility should move to; events are one of the most direct ways to make that move. This post walks through how I use it.

## What is an event?

An **event** is a simple PHP class that represents something meaningful that happened in the application — `UserRegistered`, `OrderPlaced`, `PasswordChanged`, and so on. The event itself does no work; it just says "this happened" and carries the relevant data.

A **listener** is the class that reacts to that event and performs an action. A single event can have multiple listeners.

## Example: user registration

First, let's generate the event and its listeners:

```bash
php artisan make:event UserRegistered
php artisan make:listener SendWelcomeEmail --event=UserRegistered
php artisan make:listener LogUserActivity  --event=UserRegistered
```

The event class holds only the data it needs to carry:

```php
<?php

namespace App\Events;

use App\User;
use Illuminate\Queue\SerializesModels;

class UserRegistered
{
    use SerializesModels;

    public $user;

    public function __construct(User $user)
    {
        $this->user = $user;
    }
}
```

The first listener sends the welcome email:

```php
<?php

namespace App\Listeners;

use App\Events\UserRegistered;

class SendWelcomeEmail
{
    public function handle(UserRegistered $event)
    {
        \Mail::send('emails.welcome', ['user' => $event->user], function ($m) use ($event) {
            $m->to($event->user->email)
              ->subject('Hoşgeldiniz!');
        });
    }
}
```

The second listener records the activity:

```php
<?php

namespace App\Listeners;

use App\Events\UserRegistered;
use App\ActivityLog;

class LogUserActivity
{
    public function handle(UserRegistered $event)
    {
        ActivityLog::create([
            'user_id' => $event->user->id,
            'action'  => 'registered',
        ]);
    }
}
```

## Wiring it up in the event service provider

Inside `EventServiceProvider` we map the event to its listeners:

```php
<?php

namespace App\Providers;

use Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvider;

class EventServiceProvider extends ServiceProvider
{
    protected $listen = [
        \App\Events\UserRegistered::class => [
            \App\Listeners\SendWelcomeEmail::class,
            \App\Listeners\LogUserActivity::class,
        ],
    ];
}
```

## Firing the event from the controller

The controller now focuses solely on the registration itself and knows nothing about the side effects:

```php
<?php

namespace App\Http\Controllers;

use App\Events\UserRegistered;
use App\User;
use Illuminate\Http\Request;

class RegisterController extends Controller
{
    public function store(Request $request)
    {
        $this->validate($request, [
            'email'    => 'required|email|unique:users',
            'password' => 'required|min:8',
        ]);

        $user = User::create([
            'email'    => $request->email,
            'password' => bcrypt($request->password),
        ]);

        event(new UserRegistered($user));

        return redirect('/')->with('mesaj', 'Kaydınız tamamlandı!');
    }
}
```

The single line `event(new UserRegistered($user))` kicks everything off. The controller doesn't need to know what happens next.

## What do you gain from this structure?

When a new requirement arrives — say, sending a Slack notification after registration — I just write a new listener and register it in `EventServiceProvider`. The controller and the existing listeners are untouched.

The same is true in the other direction: if I need to change the email logic, I open `SendWelcomeEmail` and nothing else. It has zero coupling to the rest of the registration flow.

There is another practical benefit: by implementing the `ShouldQueue` interface, you can push the listener onto a queue so that, for example, the email is processed in the background. All it takes is one addition to the listener class:

```php
use Illuminate\Contracts\Queue\ShouldQueue;

class SendWelcomeEmail implements ShouldQueue
{
    // ...
}
```

The controller does not change at all. That level of isolation would not be this straightforward without the decoupling the event system provides.

## Testing listeners in isolation

There is also a testability win here. Because each listener is an independent class, you can test them individually. You can construct a real `UserRegistered` event by hand and drive the listener directly — no need to trigger the full registration flow over HTTP:

```php
public function test_hosgeldin_epostasi_gonderilir()
{
    Mail::shouldReceive('send')
        ->once()
        ->with('emails.welcome', \Mockery::any(), \Mockery::any());

    $user = factory(App\User::class)->create();
    $listener = new SendWelcomeEmail();
    $listener->handle(new UserRegistered($user));
}
```

You are testing only whether the listener behaves correctly — completely isolated. That kind of isolation is simply not possible when the logic is buried inside a controller.

## Event naming matters

Event class names become part of the application's language. Past-tense names like `UserRegistered`, `OrderShipped`, and `PaymentFailed` clearly communicate "something happened" and make the system easier to understand at a glance. Names like `DoSendEmail` or `HandleUserRegistration` blur the line between an event and a listener and muddy the intent.

I did not pay enough attention to this early on and created a few poorly named event classes before realising that naming is a real signal for understanding the code. Naming an event to answer the question "what happened?" naturally makes it easier to name listeners to answer "what should happen when this does?".

## One pitfall to keep in mind

Overusing events can make tracing request flows difficult. If you start chaining every business step through a series of fired events, understanding the path a single request takes requires jumping across multiple files. The event system is a great tool for separating side effects; it is a poor tool for fragmenting the primary flow — and I try to keep that distinction in mind.

I have settled on a simple rule: if I am inside a controller method and I think "adding this here will bloat the method's responsibility," that is when the event system earns its place. But mandatory business steps within the same flow — like decrementing stock when an order is created — should not be pushed into a listener. If that step is missing, the operation cannot be considered complete; burying it in a listener makes tracing harder and complicates error handling. Events work best for side effects that are "nice to have but not required" or "could be added later."
