Laravel 13 监听器是事件系统的核心组件,本文深入探讨监听器的高级用法。
监听器基础
基础监听器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
| <?php
namespace App\Listeners;
use App\Events\OrderCreated; use Illuminate\Contracts\Queue\ShouldQueue; use Illuminate\Queue\InteractsWithQueue;
abstract class BaseListener implements ShouldQueue { use InteractsWithQueue; public string $queue = 'listeners'; public int $tries = 3; public int $backoff = 60; public int $timeout = 120; protected bool $shouldFail = false; public function failed($event, \Throwable $exception): void { \Log::error('Listener failed', [ 'listener' => get_class($this), 'event' => get_class($event), 'error' => $exception->getMessage(), ]); } public function shouldQueue($event): bool { return true; } public function viaQueue(): string { return $this->queue; } public function retryUntil(): \DateTime { return now()->addHours(24); } }
|
业务监听器
订单监听器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64
| <?php
namespace App\Listeners;
use App\Events\OrderCreated; use App\Events\OrderStatusChanged; use App\Services\Inventory\InventoryService; use App\Services\Notification\NotificationService; use App\Services\Analytics\AnalyticsService;
class UpdateInventory extends BaseListener { public function __construct( protected InventoryService $inventory ) {} public function handle(OrderCreated $event): void { foreach ($event->order->items as $item) { $this->inventory->decrement( $item->product_id, $item->quantity ); } } }
class SendOrderConfirmation extends BaseListener { public string $queue = 'mail'; public function __construct( protected NotificationService $notifications ) {} public function handle(OrderCreated $event): void { $this->notifications->send( $event->user, 'order.confirmation', [ 'order' => $event->order, 'user' => $event->user, ] ); } }
class TrackOrderAnalytics extends BaseListener { public function __construct( protected AnalyticsService $analytics ) {} public function handle(OrderCreated $event): void { $this->analytics->track('order_created', [ 'order_id' => $event->order->id, 'total' => $event->order->total, 'items_count' => $event->order->items->count(), 'user_id' => $event->user->id, ]); } }
|
队列监听器
异步监听器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49
| <?php
namespace App\Listeners;
use App\Events\PaymentProcessed; use Illuminate\Contracts\Queue\ShouldQueue; use Illuminate\Queue\InteractsWithQueue; use Illuminate\Support\Facades\Log;
class ProcessPaymentNotification implements ShouldQueue { use InteractsWithQueue; public string $queue = 'notifications'; public int $delay = 10; public int $tries = 5; public array $backoff = [30, 60, 120, 300, 600]; public function handle(PaymentProcessed $event): void { if ($event->status !== 'completed') { return; } try { $this->sendNotification($event); } catch (\Exception $e) { Log::error('Payment notification failed', [ 'order_id' => $event->order->id, 'error' => $e->getMessage(), ]); $this->release(60); } } protected function sendNotification(PaymentProcessed $event): void { } public function failed(PaymentProcessed $event, \Throwable $exception): void { Log::critical('Payment notification permanently failed', [ 'order_id' => $event->order->id, 'transaction_id' => $event->transactionId, 'error' => $exception->getMessage(), ]); } }
|
条件监听器
条件处理
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
| <?php
namespace App\Listeners;
use App\Events\OrderCreated; use Illuminate\Contracts\Queue\ShouldQueue;
class ConditionalOrderListener implements ShouldQueue { public function handle(OrderCreated $event): void { if (!$this->shouldProcess($event)) { return; } $this->process($event); } protected function shouldProcess(OrderCreated $event): bool { if ($event->order->total < 0) { return false; } if ($event->order->status === 'cancelled') { return false; } return true; } protected function process(OrderCreated $event): void { } public function shouldQueue(OrderCreated $event): bool { return $event->order->total > 100; } }
|
监听器中间件
监听器中间件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61
| <?php
namespace App\Listeners\Middleware;
use Closure;
class ListenerMiddleware { public function handle($event, Closure $next) { $startTime = microtime(true); try { $result = $next($event); $this->logExecution($event, microtime(true) - $startTime); return $result; } catch (\Throwable $e) { $this->logError($event, $e); throw $e; } } protected function logExecution($event, float $duration): void { \Log::debug('Listener executed', [ 'event' => get_class($event), 'duration' => $duration, ]); } protected function logError($event, \Throwable $e): void { \Log::error('Listener failed', [ 'event' => get_class($event), 'error' => $e->getMessage(), ]); } }
class RateLimitMiddleware { protected int $maxAttempts = 10; protected int $decayMinutes = 1; public function handle($event, Closure $next) { $key = 'listener:' . get_class($event); $attempts = cache()->get($key, 0); if ($attempts >= $this->maxAttempts) { throw new \RuntimeException('Rate limit exceeded'); } cache()->put($key, $attempts + 1, now()->addMinutes($this->decayMinutes)); return $next($event); } }
|
监听器链
链式监听器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48
| <?php
namespace App\Listeners;
use App\Events\OrderCreated;
class OrderProcessingChain { protected array $handlers = []; public function __construct() { $this->handlers = [ new ValidateOrderHandler(), new ReserveInventoryHandler(), new ProcessPaymentHandler(), new SendConfirmationHandler(), new UpdateStatisticsHandler(), ]; } public function handle(OrderCreated $event): void { foreach ($this->handlers as $handler) { if (!$handler->canHandle($event)) { continue; } $result = $handler->handle($event); if ($result === false) { break; } } } public function addHandler(HandlerInterface $handler): self { $this->handlers[] = $handler; return $this; } }
interface HandlerInterface { public function canHandle($event): bool; public function handle($event): bool; }
|
监听器优先级
优先级处理
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
| <?php
namespace App\Providers;
use App\Events\OrderCreated; use App\Listeners\ValidateOrder; use App\Listeners\ProcessPayment; use App\Listeners\SendConfirmation; use Illuminate\Foundation\Support\Providers\EventServiceProvider;
class PrioritizedEventServiceProvider extends EventServiceProvider { protected $listen = [ OrderCreated::class => [ ValidateOrder::class, ProcessPayment::class, SendConfirmation::class, ], ]; public function boot(): void { parent::boot(); $this->app->make('events')->listen( OrderCreated::class, [ValidateOrder::class, 'handle'], 100 ); $this->app->make('events')->listen( OrderCreated::class, [ProcessPayment::class, 'handle'], 50 ); $this->app->make('events')->listen( OrderCreated::class, [SendConfirmation::class, 'handle'], 10 ); } }
|
监听器测试
监听器测试
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47
| <?php
namespace Tests\Listeners;
use App\Events\OrderCreated; use App\Listeners\SendOrderConfirmation; use App\Models\Order; use App\Models\User; use App\Services\Notification\NotificationService; use Mockery; use Tests\TestCase;
class SendOrderConfirmationTest extends TestCase { public function test_it_sends_notification_to_user() { $notificationService = Mockery::mock(NotificationService::class); $user = User::factory()->create(); $order = Order::factory()->create(['user_id' => $user->id]); $notificationService->shouldReceive('send') ->once() ->with($user, 'order.confirmation', Mockery::on(function ($data) use ($order, $user) { return $data['order']->id === $order->id && $data['user']->id === $user->id; })); $listener = new SendOrderConfirmation($notificationService); $event = new OrderCreated($order, $user); $listener->handle($event); } public function test_it_queues_notification() { $notificationService = Mockery::mock(NotificationService::class); $listener = new SendOrderConfirmation($notificationService); $this->assertInstanceOf( \Illuminate\Contracts\Queue\ShouldQueue::class, $listener ); } }
|
监听器监控
监听器监控
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51
| <?php
namespace App\Services\Listeners;
use Illuminate\Support\Facades\Log;
class ListenerMonitor { protected array $stats = []; public function recordExecution(string $listener, float $duration, bool $success): void { if (!isset($this->stats[$listener])) { $this->stats[$listener] = [ 'executions' => 0, 'successes' => 0, 'failures' => 0, 'total_duration' => 0, ]; } $this->stats[$listener]['executions']++; $this->stats[$listener]['total_duration'] += $duration; if ($success) { $this->stats[$listener]['successes']++; } else { $this->stats[$listener]['failures']++; } } public function getStats(): array { return array_map(function ($stat) { return [ 'executions' => $stat['executions'], 'success_rate' => $stat['executions'] > 0 ? round($stat['successes'] / $stat['executions'] * 100, 2) : 0, 'avg_duration' => $stat['executions'] > 0 ? round($stat['total_duration'] / $stat['executions'], 2) : 0, ]; }, $this->stats); } public function getFailingListeners(): array { return array_filter($this->stats, fn($s) => $s['failures'] > 0); } }
|
总结
Laravel 13 监听器是事件系统的核心组件。通过队列监听器、条件监听器、监听器中间件和监听器链等技术,可以构建灵活、可靠的事件处理系统。