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
| <?php
namespace App\Subscribers;
use Illuminate\Events\Dispatcher;
abstract class BaseSubscriber { protected array $listeners = []; public function subscribe(Dispatcher $events): void { foreach ($this->listeners as $event => $listeners) { foreach ((array) $listeners as $listener) { $events->listen($event, $listener); } } } public function unsubscribe(Dispatcher $events): void { foreach ($this->listeners as $event => $listeners) { foreach ((array) $listeners as $listener) { $events->forget($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 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114
| <?php
namespace App\Subscribers;
use App\Events\OrderCreated; use App\Events\OrderUpdated; use App\Events\OrderCancelled; use App\Events\OrderShipped; use App\Events\OrderDelivered; use Illuminate\Events\Dispatcher;
class OrderSubscriber { public function subscribe(Dispatcher $events): array { return [ OrderCreated::class => [ 'handleOrderCreated', ], OrderUpdated::class => [ 'handleOrderUpdated', ], OrderCancelled::class => [ 'handleOrderCancelled', ], OrderShipped::class => [ 'handleOrderShipped', ], OrderDelivered::class => [ 'handleOrderDelivered', ], ]; } public function handleOrderCreated(OrderCreated $event): void { $this->updateInventory($event->order); $this->sendConfirmation($event->order); $this->trackAnalytics('order_created', $event->order); } public function handleOrderUpdated(OrderUpdated $event): void { $this->notifyChanges($event->order, $event->changes); $this->logChanges($event->order, $event->changes); } public function handleOrderCancelled(OrderCancelled $event): void { $this->restoreInventory($event->order); $this->processRefund($event->order); $this->sendCancellationNotice($event->order); } public function handleOrderShipped(OrderShipped $event): void { $this->sendShippingNotification($event->order); $this->updateTracking($event->order); } public function handleOrderDelivered(OrderDelivered $event): void { $this->sendDeliveryConfirmation($event->order); $this->requestReview($event->order); } protected function updateInventory($order): void { } protected function sendConfirmation($order): void { } protected function trackAnalytics($event, $order): void { } protected function notifyChanges($order, $changes): void { } protected function logChanges($order, $changes): void { } protected function restoreInventory($order): void { } protected function processRefund($order): void { } protected function sendCancellationNotice($order): void { } protected function sendShippingNotification($order): void { } protected function updateTracking($order): void { } protected function sendDeliveryConfirmation($order): void { } protected function requestReview($order): void { } }
|
用户订阅者
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 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119
| <?php
namespace App\Subscribers;
use App\Events\UserRegistered; use App\Events\UserUpdated; use App\Events\UserDeleted; use App\Events\UserLoggedIn; use App\Events\UserLoggedOut; use Illuminate\Events\Dispatcher;
class UserSubscriber { public function subscribe(Dispatcher $events): array { return [ UserRegistered::class => [ 'handleUserRegistered', ], UserUpdated::class => [ 'handleUserUpdated', ], UserDeleted::class => [ 'handleUserDeleted', ], UserLoggedIn::class => [ 'handleUserLoggedIn', ], UserLoggedOut::class => [ 'handleUserLoggedOut', ], ]; } public function handleUserRegistered(UserRegistered $event): void { $this->sendWelcomeEmail($event->user); $this->createUserProfile($event->user); $this->assignDefaultRole($event->user); $this->trackRegistration($event->user); } public function handleUserUpdated(UserUpdated $event): void { $this->syncExternalServices($event->user); $this->auditChanges($event->user, $event->changes); } public function handleUserDeleted(UserDeleted $event): void { $this->anonymizeData($event->user); $this->cancelSubscriptions($event->user); $this->notifyAdmin($event->user); } public function handleUserLoggedIn(UserLoggedIn $event): void { $this->updateLastLogin($event->user); $this->checkSecurityAlerts($event->user); $this->trackSession($event->user); } public function handleUserLoggedOut(UserLoggedOut $event): void { $this->cleanupSession($event->user); } protected function sendWelcomeEmail($user): void { } protected function createUserProfile($user): void { } protected function assignDefaultRole($user): void { } protected function trackRegistration($user): void { } protected function syncExternalServices($user): void { } protected function auditChanges($user, $changes): void { } protected function anonymizeData($user): void { } protected function cancelSubscriptions($user): void { } protected function notifyAdmin($user): void { } protected function updateLastLogin($user): void { } protected function checkSecurityAlerts($user): void { } protected function trackSession($user): void { } protected function cleanupSession($user): void { } }
|
支付订阅者
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 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103
| <?php
namespace App\Subscribers;
use App\Events\PaymentInitiated; use App\Events\PaymentCompleted; use App\Events\PaymentFailed; use App\Events\RefundProcessed; use Illuminate\Events\Dispatcher;
class PaymentSubscriber { public function subscribe(Dispatcher $events): array { return [ PaymentInitiated::class => [ 'handlePaymentInitiated', ], PaymentCompleted::class => [ 'handlePaymentCompleted', ], PaymentFailed::class => [ 'handlePaymentFailed', ], RefundProcessed::class => [ 'handleRefundProcessed', ], ]; } public function handlePaymentInitiated(PaymentInitiated $event): void { $this->logPaymentAttempt($event->payment); $this->validatePayment($event->payment); $this->notifyUser($event->payment, 'initiated'); } public function handlePaymentCompleted(PaymentCompleted $event): void { $this->updateOrderStatus($event->payment); $this->sendReceipt($event->payment); $this->updateUserBalance($event->payment); $this->trackRevenue($event->payment); } public function handlePaymentFailed(PaymentFailed $event): void { $this->logFailure($event->payment, $event->error); $this->notifyUser($event->payment, 'failed'); $this->alertSupport($event->payment, $event->error); } public function handleRefundProcessed(RefundProcessed $event): void { $this->updateOrderStatus($event->refund); $this->sendRefundNotification($event->refund); $this->adjustRevenue($event->refund); } protected function logPaymentAttempt($payment): void { } protected function validatePayment($payment): void { } protected function notifyUser($payment, string $status): void { } protected function updateOrderStatus($payment): void { } protected function sendReceipt($payment): void { } protected function updateUserBalance($payment): void { } protected function trackRevenue($payment): void { } protected function logFailure($payment, $error): void { } protected function alertSupport($payment, $error): void { } protected function sendRefundNotification($refund): void { } protected function adjustRevenue($refund): void { } }
|
订阅者注册
服务提供者注册
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| <?php
namespace App\Providers;
use App\Subscribers\OrderSubscriber; use App\Subscribers\UserSubscriber; use App\Subscribers\PaymentSubscriber; use Illuminate\Foundation\Support\Providers\EventServiceProvider;
class SubscriberServiceProvider extends EventServiceProvider { protected $subscribe = [ OrderSubscriber::class, UserSubscriber::class, PaymentSubscriber::class, ]; }
|
动态订阅者
动态订阅
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
| <?php
namespace App\Services\Subscribers;
use Illuminate\Events\Dispatcher;
class DynamicSubscriberManager { protected Dispatcher $dispatcher; protected array $subscribers = []; public function __construct(Dispatcher $dispatcher) { $this->dispatcher = $dispatcher; } public function register(string $subscriberClass): void { $subscriber = app($subscriberClass); $events = $subscriber->subscribe($this->dispatcher); $this->subscribers[$subscriberClass] = $events; } public function unregister(string $subscriberClass): void { if (!isset($this->subscribers[$subscriberClass])) { return; } foreach ($this->subscribers[$subscriberClass] as $event => $listeners) { foreach ($listeners as $listener) { $this->dispatcher->forget($event); } } unset($this->subscribers[$subscriberClass]); } public function getSubscribers(): array { return array_keys($this->subscribers); } public function hasSubscriber(string $subscriberClass): bool { return isset($this->subscribers[$subscriberClass]); } }
|
条件订阅者
条件处理
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
| <?php
namespace App\Subscribers;
use App\Events\OrderCreated; use Illuminate\Events\Dispatcher;
class ConditionalOrderSubscriber { protected array $conditions = []; public function __construct() { $this->conditions = [ 'handleHighValueOrder' => fn($event) => $event->order->total > 1000, 'handleInternationalOrder' => fn($event) => $event->order->is_international, 'handleSubscriptionOrder' => fn($event) => $event->order->has_subscription, ]; } public function subscribe(Dispatcher $events): array { return [ OrderCreated::class => [ 'handleOrderCreated', ], ]; } public function handleOrderCreated(OrderCreated $event): void { foreach ($this->conditions as $method => $condition) { if ($condition($event) && method_exists($this, $method)) { $this->{$method}($event); } } } protected function handleHighValueOrder(OrderCreated $event): void { } protected function handleInternationalOrder(OrderCreated $event): void { } protected function handleSubscriptionOrder(OrderCreated $event): void { } }
|
订阅者测试
订阅者测试
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 Tests\Subscribers;
use App\Events\OrderCreated; use App\Subscribers\OrderSubscriber; use App\Models\Order; use App\Models\User; use Illuminate\Support\Facades\Event; use Tests\TestCase;
class OrderSubscriberTest extends TestCase { protected OrderSubscriber $subscriber; protected function setUp(): void { parent::setUp(); $this->subscriber = new OrderSubscriber(); } public function test_it_subscribes_to_order_events() { $events = $this->subscriber->subscribe(app('events')); $this->assertArrayHasKey(OrderCreated::class, $events); } public function test_it_handles_order_created_event() { $user = User::factory()->create(); $order = Order::factory()->create(['user_id' => $user->id]); $event = new OrderCreated($order, $user); $this->subscriber->handleOrderCreated($event); $this->assertDatabaseHas('inventory_logs', [ 'order_id' => $order->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
| <?php
namespace App\Services\Subscribers;
class SubscriberMonitor { protected array $stats = []; public function recordExecution(string $subscriber, string $method, float $duration): void { $key = "{$subscriber}::{$method}"; if (!isset($this->stats[$key])) { $this->stats[$key] = [ 'count' => 0, 'total_duration' => 0, ]; } $this->stats[$key]['count']++; $this->stats[$key]['total_duration'] += $duration; } public function getStats(): array { return array_map(function ($stat) { return [ 'count' => $stat['count'], 'avg_duration' => $stat['count'] > 0 ? $stat['total_duration'] / $stat['count'] : 0, ]; }, $this->stats); } }
|
总结
Laravel 13 订阅者模式提供了组织多个事件监听器的优雅方式。通过将相关事件处理逻辑集中在一个类中,可以提高代码的可维护性和可读性。