Eventos e listeners no Laravel
1. Introdução aos Eventos e Listeners
Eventos e listeners no Laravel implementam o padrão Observer, permitindo que ações específicas disparem reações em cadeia sem acoplamento direto entre os componentes. Esse mecanismo é essencial para construir aplicações modulares, escaláveis e de fácil manutenção.
O fluxo básico funciona assim: um evento é disparado em algum ponto da aplicação (ex.: "pedido foi criado"), e todos os listeners registrados para aquele evento executam suas ações (ex.: enviar e-mail, atualizar estoque, registrar log). O Laravel gerencia todo o roteamento entre eventos e listeners.
2. Criando Eventos e Listeners
O Laravel oferece comandos Artisan para gerar as classes base:
// Gerar um evento
php artisan make:event OrderShipped
// Gerar um listener
php artisan make:listener SendShipmentNotification --event=OrderShipped
A estrutura de uma classe de evento é simples:
<?php
namespace App\Events;
use App\Models\Order;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Queue\SerializesModels;
class OrderShipped
{
use Dispatchable, SerializesModels;
public $order;
public function __construct(Order $order)
{
$this->order = $order;
}
}
Já o listener contém a lógica a ser executada:
<?php
namespace App\Listeners;
use App\Events\OrderShipped;
use Illuminate\Support\Facades\Log;
class SendShipmentNotification
{
public function handle(OrderShipped $event): void
{
$order = $event->order;
Log::info("Notificação de envio para o pedido #{$order->id}");
// Aqui você enviaria e-mail, SMS, notificação push, etc.
}
}
O registro manual no EventServiceProvider conecta os dois:
protected $listen = [
OrderShipped::class => [
SendShipmentNotification::class,
UpdateInventory::class,
],
];
3. Registro Automático e Descoberta de Eventos
Além do mapeamento manual, o Laravel 8+ oferece descoberta automática de eventos. Basta configurar no EventServiceProvider:
public function shouldDiscoverEvents(): bool
{
return true;
}
O framework então escaneia seus listeners e eventos, associando-os automaticamente. Para otimizar performance em produção, use os comandos:
// Criar cache de eventos
php artisan event:cache
// Limpar cache de eventos
php artisan event:clear
Boas práticas: Use mapeamento manual em projetos complexos para ter controle explícito. A descoberta automática é ideal para prototipagem ou projetos menores.
4. Disparando Eventos
Existem duas formas principais de disparar eventos:
// Usando a facade
use Illuminate\Support\Facades\Event;
Event::dispatch(new OrderShipped($order));
// Usando o helper global
event(new OrderShipped($order));
O payload do evento é passado via construtor. Exemplo prático com dados do pedido:
class OrderShipped
{
use Dispatchable;
public function __construct(
public Order $order,
public string $trackingCode,
public array $items
) {}
}
// Disparando
event(new OrderShipped($order, 'BR123456789', $order->items->toArray()));
O disparo pode ser síncrono (padrão) ou assíncrono usando filas.
5. Listeners com Fila (Queueable)
Para processamento assíncrono, implemente a interface ShouldQueue:
<?php
namespace App\Listeners;
use App\Events\OrderShipped;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Queue\InteractsWithQueue;
class SendShipmentNotification implements ShouldQueue
{
use InteractsWithQueue;
public $connection = 'redis';
public $queue = 'notifications';
public $delay = 10; // segundos
public function handle(OrderShipped $event): void
{
// Envio de e-mail pesado aqui
}
public function failed(OrderShipped $event, \Throwable $exception): void
{
// Registrar falha no envio
Log::error("Falha ao enviar notificação: {$exception->getMessage()}");
}
public function retryUntil(): \DateTime
{
return now()->addMinutes(5);
}
}
Isso permite que o envio de e-mails ou processamento de imagens não bloqueie a resposta HTTP.
6. Eventos Eloquent e Modelos
O Eloquent dispara eventos automaticamente durante operações no banco: created, updated, deleted, saved, restored, etc. Você pode mapeá-los no modelo:
<?php
namespace App\Models;
use App\Events\UserCreated;
use Illuminate\Database\Eloquent\Model;
class User extends Model
{
protected $dispatchesEvents = [
'created' => UserCreated::class,
];
}
Observers vs. Eventos: Observers agrupam vários listeners para um modelo em uma única classe. Use observers quando várias ações precisam reagir a diferentes eventos do mesmo modelo. Use eventos para ações transversais que afetam múltiplos modelos.
Exemplo de listener para log ao criar usuário:
class LogUserCreation
{
public function handle(UserCreated $event): void
{
Log::channel('audit')->info("Usuário criado: {$event->user->email}", [
'user_id' => $event->user->id,
'timestamp' => now()
]);
}
}
7. Listeners Condicionais e Eventos com Filtros
Listeners podem interromper a propagação de eventos para listeners subsequentes:
public function handle(OrderShipped $event): void
{
if ($event->order->status !== 'paid') {
return; // Não faz nada se não estiver pago
}
$event->stopPropagation(); // Impede que outros listeners executem
$this->sendNotification($event->order);
}
Para testes, o Laravel fornece Event::fake():
public function test_order_shipped_triggers_notification()
{
Event::fake();
$order = Order::factory()->create();
event(new OrderShipped($order));
Event::assertDispatched(OrderShipped::class, function ($event) use ($order) {
return $event->order->id === $order->id;
});
}
8. Boas Práticas e Performance
- Mantenha listeners pequenos — cada listener deve fazer apenas uma coisa (princípio da responsabilidade única)
- Evite acoplamento — eventos não devem conhecer detalhes dos listeners, e vice-versa
- Use cache de eventos em produção —
php artisan event:cacheacelera o registro - Monitore filas — use Laravel Horizon ou ferramentas nativas para acompanhar listeners em fila
- Priorize listeners críticos — use filas diferentes para notificações, logs e processamento pesado
Exemplo de listener otimizado:
class UpdateInventory implements ShouldQueue
{
public $queue = 'inventory';
public function handle(OrderShipped $event): void
{
foreach ($event->order->items as $item) {
$item->product->decrement('stock', $item->quantity);
}
}
}
Eventos e listeners no Laravel oferecem uma arquitetura elegante para desacoplar lógicas de negócio. Quando bem implementados, tornam o código mais testável, modular e preparado para escalar.
Referências
- Laravel Official Documentation: Events — Documentação oficial completa sobre eventos e listeners no Laravel
- Laravel Daily: Laravel Events & Listeners Tutorial — Tutorial prático com exemplos do mundo real
- Laracasts: Events and Listeners — Vídeo-aula gratuita sobre eventos no Laravel
- Laravel News: Understanding Laravel Events — Artigo aprofundado sobre padrões de eventos no ecossistema Laravel
- Stack Overflow: Laravel Events vs Observers — Discussão técnica sobre quando usar eventos vs observers
- Laravel Bootcamp: Events & Queues — Tutorial oficial do Laravel Bootcamp sobre eventos com filas