Skip to main content
Create your own
Lesson illustration

Event Handling: Creating and Dispatching Events

Hello and welcome to a new module in your Laravel mastery course!

In the previous module, we focused on background processing, culminating in a lesson on how to handle massive data imports using queues and job batching. That was all about decoupling slow or heavy tasks from the main web request to improve performance and reliability.

Today, we're starting Module 10, "Real-Time Applications with Events & Broadcasting," by exploring a related but broader concept: Laravel's event system. While queues are about what to do later, events are about announcing that something significant has happened. This allows different, unrelated parts of your application to react to that occurrence without being tightly coupled together.

This lesson directly addresses the learning outcome: Create and dispatch application events with corresponding listeners. By the end of our session, you'll understand the "why" behind events and be able to implement the full event-listener cycle in your Laravel projects.


1. The "Why": Decoupling Your Application Logic

Imagine you're building a feature where a new user registers. In your RegisteredUserController, you might need to:

  1. Create the user record in the database.
  2. Send a welcome email.
  3. Assign a default role.
  4. Log the registration event for analytics.
  5. Notify an admin via Slack.

If you put all of this logic directly into your controller, it quickly becomes bloated, hard to read, and difficult to maintain. What if you need to add a sixth step later? You'd have to modify the controller again. This is called tight coupling.

Laravel's event system provides an elegant solution using the Observer design pattern. Instead of your controller doing everything, it simply "announces" an event, like UserRegistered. Other parts of your application, called listeners, can subscribe to this event and perform their specific tasks.

User Registration with Asynchronous Event Dispatching and Queues
This diagram illustrates the decoupling process. The application dispatches a `UserRegistered` event and can immediately respond to the user. The actual work, like sending an email, is handled by a listener, often asynchronously in a queue, without blocking the main process.

This approach has several key benefits:

  • Decoupling: Your RegisteredUserController only needs to know about creating a user and dispatching an event. It doesn't need to know anything about sending emails or Slack messages.
  • Extensibility: Need to add a new action when a user registers? Just create a new listener. You don't have to touch the original controller code.
  • Readability: Your code becomes cleaner and more focused on a single responsibility.

The core idea is explained well in the official documentation.

Events - Laravel 12.x

Let's start with the official Laravel documentation to get a high-level overview of the concept.

Read the 'Introduction' section. Focus on the example provided: instead of coupling order processing code with Slack notification code, you raise an OrderShipped event.


2. The Core Mechanics: The Event-Listener Lifecycle

Implementing this pattern in Laravel involves a clear, repeatable workflow. Let's break it down step-by-step.

Step 1: Generate the Classes

Laravel provides Artisan commands to create the necessary files for you. These files will be placed in the app/Events and app/Listeners directories.




# Create an event class
php artisan make:event UserRegistered




# Create a listener class for that event
php artisan make:listener SendWelcomeEmail --event=UserRegistered

Step 2: Define the Event

An event class is primarily a data container. Its job is to hold any relevant information about what happened. You pass this data through the event's constructor. Since you're familiar with PHP, you'll find constructor property promotion makes this very concise.

// app/Events/UserRegistered.php

namespace App\Events;

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

class UserRegistered
{
    use Dispatchable, SerializesModels;

    /**
     * Create a new event instance.
     */
    public function __construct(
        public User $user
    ) {}
}

Here, the UserRegistered event holds the User model that was just created. The SerializesModels trait is important—it ensures that if your event is queued, the Eloquent model is serialized and re-hydrated efficiently.

Step 3: Define the Listener

The listener is where the actual work gets done. Its handle method receives the event object, giving it access to the data it contains.

// app/Listeners/SendWelcomeEmail.php

namespace App\Listeners;

use App\Events\UserRegistered;
use Illuminate\Support\Facades\Mail;
use App\Mail\WelcomeEmail;

class SendWelcomeEmail
{
    /**
     * Create the event listener.
     */
    public function __construct() {}

    /**
     * Handle the event.
     */
    public function handle(UserRegistered $event): void
    {
        // Access the user from the event object
        $user = $event->user;

        // Perform the action
        Mail::to($user->email)->send(new WelcomeEmail($user));
    }
}

Step 4: Register the Listener

How does Laravel know that SendWelcomeEmail should run when UserRegistered is dispatched? There are two ways:

  1. Automatic Discovery (Recommended): By default, Laravel automatically scans your app/Listeners directory. It looks at the handle method of each listener and uses the type-hinted event (UserRegistered in our example) to figure out which event it listens for. This is the modern, zero-configuration approach.

  2. Manual Registration: For older versions of Laravel or more complex use cases, you can explicitly map events to their listeners in the app/Providers/EventServiceProvider.php file.

// app/Providers/EventServiceProvider.php
protected $listen = [
    'App\Events\UserRegistered' => [
        'App\Listeners\SendWelcomeEmail',
        'App\Listeners\NotifyAdminOfNewUser', // You can have multiple listeners!
    ],
];

Even if you use automatic discovery, the event:list Artisan command is useful to see all registered event-listener pairs.

Step 5: Dispatch the Event

Finally, from your controller (or anywhere else in your code), you can dispatch the event. You can use the dispatch static method on the event class or the global event() helper function. Any arguments you pass will be sent to the event's constructor.

// In a controller, after creating a user...
use App\Events\UserRegistered;

// ...

$user = User::create([...]);

// Dispatch the event and pass the new user model
UserRegistered::dispatch($user); 

// Or using the helper
// event(new UserRegistered($user));

return response()->json(['message' => 'User registered successfully!']);

3. A Practical Walkthrough

Now let's see all these pieces come together in a practical demonstration. The official Laravel "Bootcamp" series has a short video that perfectly walks through this entire process.

09 - Events & Listeners

This video from the official Laravel channel will guide you through building a simple event-listener system from scratch.

Watch the video from start to finish (00:00 - 06:10). As you watch, identify each of the 5 steps we just discussed: Generating files (01:19) Defining the event with an $order property (03:52) Defining the listener and its handle method (02:23) Registering the listener manually in EventServiceProvider (04:42) Dispatching the event from the route closure (04:22)


4. Events vs. Model Observers

As someone with some Laravel experience, you might be wondering: "How is this different from a Model Observer?" That's an excellent question.

  • Events are a general-purpose system. You can dispatch them from anywhere: controllers, jobs, services, or even other listeners. They represent any significant action in your application, not just database changes.
  • Model Observers are a specific implementation of the event system that are tied directly to the Eloquent model lifecycle. They listen for events like creating, created, updating, updated, deleting, and deleted.

This blog post from Honeybadger has a great, concise summary.

A guide to Laravel events and listeners - Honeybadger.io

To clarify the difference, let's read a short section from a Honeybadger blog post.

Read the section titled 'Laravel observers vs. Laravel events'. The key takeaway is that observers are for things that only occur in Eloquent Models, while events are general-purpose and can be used anywhere.

In short, think of Observers as a convenient shortcut for dispatching model-related events. We will cover Observers in detail in our next lesson.

To solidify your understanding, here is a helpful flowchart summarizing the entire process we've covered today.

How to Use Laravel Events and Listeners Jointly
This flowchart visually maps the nine key steps in implementing events and listeners, from initial setup to automatic discovery.

Conclusion

You have now learned the fundamentals of creating and dispatching application events in Laravel. This is a core architectural concept that enables you to build cleaner, more maintainable, and scalable applications.

Key Takeaways:

  • Events and listeners implement the Observer pattern to decouple application components.
  • The lifecycle is straightforward: generate classes, define the event as a data container, implement logic in the listener's handle method, register the pairing (usually automatically), and dispatch the event.
  • A single event can have multiple listeners, allowing you to orchestrate complex workflows without cluttering your controllers.
  • Events are a general-purpose system, whereas Model Observers (which we'll see next) are a specialized convenience for Eloquent model lifecycle hooks.

Up Next:

Now that you understand the general event system, we'll dive into that specialized use case. In the next lesson, you will learn to use Eloquent observers to automatically dispatch events on model lifecycle hooks. This will allow you to effortlessly hook into actions like created or updated on your models, further cleaning up your code and centralizing your business logic.

Can't find a good explanation? Sign up and we'll make it for you

Sign up