Laravel Events and Listeners
Laravel Events and Listeners: Tips & Tricks
Laravel Events and Listeners: Tips & Tricks
Tip: Use Event Subscribers for Related Listeners
Instead of registering multiple listeners, use a subscriber:
class UserEventSubscriber
{
public function handleLogin($event) { /* ... */ }
public function handleLogout($event) { /* ... */ }
public function subscribe(Dispatcher $events): void
{
$events->listen(UserLoggedIn::class, [self::class, 'handleLogin']);
$events->listen(UserLoggedOut::class, [self::class, 'handleLogout']);
}
}
Gotcha: Queued Listeners Run Asynchronously
If a listener implements ShouldQueue, it runs in the background. Errors won't bubble up to the original request.
Tip: Use Closures for Simple Listeners
Event::listen(function (OrderShipped $event) {
Mail::to($event->order->user)->send(new ShippedMail($event->order));
});
No need for a full listener class for one-liners.
Gotcha: Event::fake() Breaks Model Events
When you call Event::fake() in tests, it also fakes model events (creating, saved, etc.). Use Event::fakeExcept() to exclude them.
Tip: Wildcard Listeners
Event::listen('user.*', function ($eventName, array $data) {
Log::info("User event: {$eventName}");
});
Catches all events starting with user..
Tip: Use withoutEvents() for Bulk Operations
User::withoutEvents(function () {
User::query()->update(['active' => true]);
});
Skips all model events during the operation. Great for migrations and bulk updates.
Gotcha: Event Order is Not Guaranteed
Unless listeners are queued, they run in registration order. But queued listeners can execute in any order.
Tip: Use route:cache Carefully
php artisan route:cache is fast, but it doesn't work with closure-based routes. Every time you cache routes, Laravel serializes them. If you have Route::redirect() or closure callbacks, the cache breaks. Stick to controller-based routes in production.
Tip: Model APP_KEY Rotation
Rotating APP_KEY invalidates all encrypted data — cookies, encrypted DB columns, and password reset tokens. If you must rotate (e.g., after a leak), plan a migration that re-encrypts existing data with the new key.
Gotcha: Local Scope Leaks
Global scopes defined in booted() apply to ALL queries on that model — including relationships. An innocent User::all() in admin panel might exclude soft-deleted users if a global scope is active.
Senior Insight
Event-driven architecture in Laravel is powerful but easy to misuse. The pitfall I see most often is over-using events for things that should be direct method calls — every event adds indirection and makes the code harder to trace. My rule: use events only when the listener belongs to a different domain or when multiple unrelated listeners need to react. For everything else, just call the method directly. Your future self will thank you when debugging.
Source: Laravel News (https://laravel-news.com/), Freek.dev (https://freek.dev/tags/laravel), Spatie Blog (https://spatie.be/blog)