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 监听器是事件系统的核心组件。通过队列监听器、条件监听器、监听器中间件和监听器链等技术,可以构建灵活、可靠的事件处理系统。