Laravel Events and Listeners: Complete Guide (Basic to Advanced)
Laravel's event system provides a simple observer pattern implementation, allowing you to subscribe and listen for various events that occur in your application. This guide will take you from the basics to advanced implementations of Laravel's event system.
Table of Contents
- What are Events and Listeners?
- Basic Setup
- Creating Events
- Creating Listeners
- Registering Events and Listeners
- Dispatching Events
- Event Subscribers
- Queued Listeners
- Testing Events
- Advanced Patterns
What are Events and Listeners?
In Laravel, events are a way to decouple various aspects of your application. When an event occurs (is "dispatched"), all registered listeners (or subscribers) are notified.
Events are typically classes that represent something that happened in your application (e.g., UserRegistered
, OrderShipped
).
Listeners are classes that respond to events by performing some action (e.g., sending a welcome email when a user registers).
Basic Setup
Laravel's event system is ready to use out of the box. The main configuration file is config/event.php
, but you'll typically work with the EventServiceProvider
.
The EventServiceProvider
is located at app/Providers/EventServiceProvider.php
and looks like this:
<?php
namespace App\Providers;
use Illuminate\Support\Facades\Event;
use Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvider;
class EventServiceProvider extends ServiceProvider
{
/**
* The event listener mappings for the application.
*
* @var array
*/
protected $listen = [
// Example:
// 'App\Events\OrderShipped' => [
// 'App\Listeners\SendShipmentNotification',
// ],
];
/**
* Register any events for your application.
*
* @return void
*/
public function boot()
{
parent::boot();
}
}
Creating Events
Events are simple PHP classes that typically contain information about what happened. You can generate an event using Artisan:
php artisan make:event UserRegistered
This will create a new event class in app/Events/UserRegistered.php
:
<?php
namespace App\Events;
use Illuminate\Broadcasting\Channel;
use Illuminate\Queue\SerializesModels;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
class UserRegistered
{
use Dispatchable, InteractsWithSockets, SerializesModels;
public $user;
/**
* Create a new event instance.
*
* @param \App\User $user
* @return void
*/
public function __construct($user)
{
$this->user = $user;
}
}
Key traits used in events:
Dispatchable
: Allows the event to be dispatchedInteractsWithSockets
: Used for broadcastingSerializesModels
: Allows Eloquent models to be serialized
Creating Listeners
Listeners handle the logic that should occur when an event is dispatched. Create a listener with Artisan:
php artisan make:listener SendWelcomeEmail --event=UserRegistered
This creates app/Listeners/SendWelcomeEmail.php
:
<?php
namespace App\Listeners;
use App\Events\UserRegistered;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Queue\InteractsWithQueue;
class SendWelcomeEmail
{
/**
* Create the event listener.
*
* @return void
*/
public function __construct()
{
//
}
/**
* Handle the event.
*
* @param UserRegistered $event
* @return void
*/
public function handle(UserRegistered $event)
{
// Send welcome email to $event->user
}
}
Registering Events and Listeners
There are several ways to register event/listener relationships:
1. Via the EventServiceProvider
Add your events and listeners to the $listen
array in EventServiceProvider
:
protected $listen = [
'App\Events\UserRegistered' => [
'App\Listeners\SendWelcomeEmail',
'App\Listeners\CreateUserProfile',
],
];
2. Manually with the Event Facade
You can register listeners in the boot
method of EventServiceProvider
:
public function boot()
{
parent::boot();
Event::listen(
'App\Events\UserRegistered',
'App\Listeners\SendWelcomeEmail'
);
}
3. Using Wildcards
You can register listeners that handle multiple events using wildcards:
Event::listen('App\Events\*', function ($eventName, array $data) {
// Handle all App\Events events
});
Dispatching Events
You can dispatch events in several ways:
1. Using the event() Helper
event(new UserRegistered($user));
2. Using the Event Facade
use Illuminate\Support\Facades\Event;
Event::dispatch(new UserRegistered($user));
3. Using the event's dispatch Method
UserRegistered::dispatch($user);
event(new SomeEvent());
Event Subscribers
Subscribers are classes that may subscribe to multiple events from within the subscriber class itself. Here's how to create one:
1. Create a Subscriber Class
<?php
namespace App\Listeners;
class UserEventSubscriber
{
/**
* Handle user registration events.
*/
public function handleUserRegistration($event) {
// Send welcome email
}
/**
* Handle user login events.
*/
public function handleUserLogin($event) {
// Log login activity
}
/**
* Register the listeners for the subscriber.
*
* @param \Illuminate\Events\Dispatcher $events
* @return void
*/
public function subscribe($events)
{
$events->listen(
'App\Events\UserRegistered',
[UserEventSubscriber::class, 'handleUserRegistration']
);
$events->listen(
'App\Events\UserLoggedIn',
[UserEventSubscriber::class, 'handleUserLogin']
);
}
}
2. Register the Subscriber
Add the subscriber to the $subscribe
property in EventServiceProvider
:
protected $subscribe = [
'App\Listeners\UserEventSubscriber',
];
Queued Listeners
For time-consuming tasks (like sending emails), you can queue listeners:
<?php
namespace App\Listeners;
use App\Events\UserRegistered;
use Illuminate\Contracts\Queue\ShouldQueue;
class SendWelcomeEmail implements ShouldQueue
{
public function handle(UserRegistered $event)
{
// This will be queued
}
}
Key points about queued listeners:
- Implement the
ShouldQueue
interface - Will be processed by your queue worker
- Can specify queue connection, name, and delay
Customizing Queue Behavior
class SendWelcomeEmail implements ShouldQueue
{
public $connection = 'sqs';
public $queue = 'emails';
public $delay = 60;
// ...
}
Handling Failed Jobs
public function failed(UserRegistered $event, $exception)
{
// Handle failure
}
Testing Events
Laravel provides helpers for testing events:
1. Asserting an Event was Dispatched
$this->expectsEvents(App\Events\UserRegistered::class);
2. Asserting an Event was Not Dispatched
$this->doesntExpectEvents(App\Events\UserRegistered::class);
3. Fake Events for Testing
// Fake all events
Event::fake();
// Fake specific events
Event::fake([
UserRegistered::class,
]);
// Assertions
Event::assertDispatched(UserRegistered::class);
Event::assertDispatched(UserRegistered::class, function ($event) use ($user) {
return $event->user->id === $user->id;
});
Event::assertNotDispatched(OrderShipped::class);
Advanced Patterns
1. Event Sourcing
Store all changes to application state as a sequence of events:
// Instead of:
$user->update(['status' => 'active']);
// Do:
event(new UserStatusChanged($user, 'active'));
2. Domain Events
Create events that represent important business occurrences:
class OrderCompleted
{
public $order;
public function __construct(Order $order)
{
$this->order = $order;
}
}
3. Event Buses
For complex systems, consider using a dedicated event bus package like:
- SimpleBus
- Tactician
4. Event Sourcing with Projections
Rebuild application state from stored events:
// Rebuild user state
$events = Event::where('aggregate_id', $userId)->get();
$user = new User();
foreach ($events as $event) {
$user->apply($event);
}
Conclusion
Laravel's event system provides a powerful way to decouple components of your application. By following the patterns outlined in this guide, you can create applications that are more maintainable, scalable, and testable.
Key takeaways:
- Events represent something that happened in your application
- Listeners respond to events with specific actions
- The event system helps maintain separation of concerns
- Queued listeners improve performance for slow operations
- Laravel provides excellent testing support for events
Start with simple events and listeners, then gradually explore more advanced patterns as your application grows in complexity.
No comments:
Post a Comment