Decoupling Laravel Logic with Events and Listeners

The Problem of Code Entanglement

When you first build an action, like purchasing a ticket, the code is usually clean. But as your application scales, that single job begins to accumulate unrelated responsibilities. Suddenly, your ProcessTicketPurchase job is saving records, emailing users, alerting admins, and updating marketing analytics. This creates a "telephone game" effect where a single class becomes a discombobulated mess. To keep your codebase maintainable, you must separate the core action from its side effects.

Prerequisites

To follow this guide, you should be comfortable with

and the
Laravel
framework. Familiarity with
Artisan
commands and basic
Object-Oriented Programming
principles is essential.

Key Libraries & Tools

  • Laravel
    : A robust PHP framework providing the built-in Event Dispatcher.
  • Artisan
    : The command-line interface used to scaffold event and listener classes.
  • Livewire
    : Used in the examples to handle frontend logic and dispatch events.

Code Walkthrough

First, we generate the event class. Think of an event as a simple data container.

// Generate the event
php artisan make:event TicketPurchased

Inside the event, we define the data it needs to carry, such as the User and the ticket type string. By keeping this class logic-free, we ensure it only serves as a messenger.

public function __construct(
    public User $user, 
    public string $type
) {}

Next, we create the listener. This is where the side-effect logic (like sending an email) lives. Using

during generation makes it easy to link the listener to our specific event.

// Generate the listener
php artisan make:listener SendTicketPurchasedNotification

In the handle method of the listener, we access the data from the event instance to perform the task.

public function handle(TicketPurchased $event): void
{
    $event->user->notify(new TicketPurchasedNotification($event->type));
}

Syntax Notes

uses Constructor Property Promotion in event classes to reduce boilerplate. When you declare public variables in the constructor, the framework automatically assigns them. Additionally, if you pass an
Eloquent
model into an event,
Laravel
handles the serialization, ensuring the model is correctly restored if the event is queued.

Practical Examples

Beyond manual dispatching, you can use Model Events. By defining a $dispatchesEvents property on a model, you can trigger actions automatically whenever a record is created, updated, or deleted. This is perfect for audit logs or clearing caches without cluttering your controllers.

Tips & Gotchas

Always consider if a listener should be queued. If the listener performs an external API call or sends an email, implement the ShouldQueue interface. This prevents your user from waiting for these background tasks to finish before their page reloads. Keep your events focused on what happened, and let listeners decide what to do about it.

3 min read