事件系統
事件系統
事件系統介紹Laravel 的事件提供了一個簡單的觀察者實現,能夠訂閱和監聽應用中發生的各種事件。事件類別通常存放在app/Events 目錄下,而這些事件類別的監聽器則存放在
app/Listeners 目錄下。如果在你的應用程式中你沒有看到這些目錄,不用擔心,它們會在你使用 Artisan 控制台命令產生事件與監聽器的時候自動建立。
OrderShipped 事件,而不是將訂單處理程式碼和 Slack 通知程式碼耦合在一起。
EventServiceProvider 為註冊所有的事件監聽器提供了一個便利的場所。其中,
listen 屬性包含了所有事件 (鍵) 以及事件對應的監聽器 (值) 的陣列。當然,你可以根據應用程式的需要,新增多個事件到
listen 屬性包含的陣列中。舉個例子,我們來新增一個
OrderShipped 事件:
/** * 应用程序的事件监听器映射 * * @var array */ protected $listen = [ 'App\Events\OrderShipped' => [ 'App\Listeners\SendShipmentNotification', ], ];##
產生事件 & 監聽器
當然,手動建立事件和監聽器的檔案是件麻煩事。而在這裡,你只需要將監聽器和事件加入到 EventServiceProvider
中,而後使用 event:generate
指令。這個指令會產生在 EventServiceProvider
中列出的所有事件和監聽器。當然,已經存在的事件和監聽器將保持不變:
php artisan event:generate
#手動註冊事件
通常,事件是在EventServiceProvider
的$listen
陣列中註冊;然而,你也可以在EventServiceProvider
的boot
方法中手動註冊基於閉套件的這些事件:
/** * 注册应用中的其它事件 * * @return void */ public function boot(){ parent::boot(); Event::listen('event.name', function ($foo, $bar) { // }); }
通配符事件監聽器
你可以在註冊監聽器時使用*
作為通配符參數,這樣可以在同一個監聽器上捕捉多個事件。通配符監聽器接收事件名稱作為其第一個參數,並將整個事件資料數組作為其第二個參數:
Event::listen('event.*', function ($eventName, array $data) { // });
定義事件
事件類別是一個保存與事件相關資訊的容器。例如,假設我們產生的 OrderShipped
事件接收一個 Eloquent ORM 物件:
<?php namespace App\Events; use App\Order; use Illuminate\Queue\SerializesModels; class OrderShipped{ use SerializesModels; public $order; /** * 创建一个事件实例。 * * @param \App\Order $order * @return void */ public function __construct(Order $order) { $this->order = $order; } }
如你所見,這個事件類別中沒有包含其它邏輯。它只是一個購買的 Order
的實例的容器。如果使用 PHP 的 serialize
函數序列化事件對象,事件使用的 SerializesModels
trait 將會優雅地序列化任何 Eloquent 模型。
#定義監聽器
接下來,讓我們來看看例子中事件的監聽器。事件監聽器在 handle
方法中接收實例。 event:generate
指令會自動載入正確的事件類,並且在 handle
方法中加入事件的類型提示。在handle
方法中,你可以執行任何必要的回應事件的操作:
<?php namespace App\Listeners; use App\Events\OrderShipped; class SendShipmentNotification{ /** * 创建事件监听器。 * * @return void */ public function __construct() { // } /** * 处理事件。 * * @param \App\Events\OrderShipped $event * @return void */ public function handle(OrderShipped $event) { // 使用 $event->order 来访问 order ... } }
{tip} 你的事件監聽器也可以在建構子中加入任何依賴關係的類型提示。所有的事件監聽器都是透過 Laravel 的 服務容器 來解析的,因此所有的依賴都會被自動注入。
停止事件傳播
有時,你可以透過在監聽器的handle
方法中傳回false
來阻止事件被其它的監聽器取得。
事件監聽器佇列
如果你的監聽器中要執行諸如發送電子郵件或進行 HTTP 請求等比較慢的任務,你可以選擇將其丟給佇列處理。在開始使用佇列監聽器之前,請確保在你的伺服器或本機開發環境中能夠 設定佇列 並啟動一個佇列監聽器。
要指定監聽器啟動佇列,你可以在監聽器類別中增加 ShouldQueue
介面。由 Artisan 指令 event:generate
產生的監聽器已經將此介面匯入到目前命名空間中,因此你可以直接使用:
<?php namespace App\Listeners; use App\Events\OrderShipped; use Illuminate\Contracts\Queue\ShouldQueue; class SendShipmentNotification implements ShouldQueue{ // }
就是這個!當這個監聽器被事件呼叫時,事件調度器會自動使用 Laravel 的 佇列系統。如果在佇列中執行監聽器時沒有拋出異常,任務會在執行完成後自動從佇列中刪除。
自訂佇列連線& 佇列名稱
如果你想要自訂事件監聽器所使用的佇列的連線和名稱,你可以在監聽器類別中定義$connection
, $queue
或$delay
屬性:
<?php namespace App\Listeners; use App\Events\OrderShipped; use Illuminate\Contracts\Queue\ShouldQueue; class SendShipmentNotification implements ShouldQueue{ /** * The name of the connection the job should be sent to. * * @var string|null */ public $connection = 'sqs'; /** * The name of the queue the job should be sent to. * * @var string|null */ public $queue = 'listeners'; /** * The time (seconds) before the job should be processed. * * @var int */ public $delay = 60; }
#手動存取佇列如果你需要手動存取監聽器下面佇列任務的
和release 方法,你可以透過使用
Illuminate\ Queue\InteractsWithQueue<?php namespace App\Listeners; use App\Events\OrderShipped; use Illuminate\Queue\InteractsWithQueue; use Illuminate\Contracts\Queue\ShouldQueue; class SendShipmentNotification implements ShouldQueue{ use InteractsWithQueue; /** * 处理事件。 * * @param \App\Events\OrderShipped $event * @return void */ public function handle(OrderShipped $event) { if (true) { $this->release(30); } } }
處理失敗任務
有時事件監聽器的佇列任務可能會失敗。如果監聽器的佇列任務超過了佇列中定義的最大嘗試次數,則會在監聽器上呼叫failedeventfailed
方法。
<?php namespace App\Http\Controllers; use App\Order;use App\Events\OrderShipped; use App\Http\Controllers\Controller; class OrderController extends Controller{ /** * 将传递过来的订单发货 * * @param int $orderId * @return Response */ public function ship($orderId) { $order = Order::findOrFail($orderId); // 订单发货逻辑 ... event(new OrderShipped($order)); } }######{tip} 在測試時,只需要斷言特定事件被分發,而不需要真正觸發監聽器。 Laravel 的 內建測試輔助函數 可以輕鬆做到這一點。 ###########################事件訂閱者#################### #
編寫事件訂閱者
事件訂閱者是可以在自身內部訂閱多個事件的類,也就是能夠在單一類別中定義多個事件處理器。訂閱者應該定義一個 subscribe
方法,這個方法接收一個事件分發器實例。你可以呼叫給定事件分發器上的listen
方法來註冊事件監聽器:
<?php namespace App\Listeners; class UserEventSubscriber{ /** * 处理用户登录事件。 */ public function onUserLogin($event) {} /** * 处理用户注销事件。 */ public function onUserLogout($event) {} /** * 为订阅者注册监听器 * * @param \Illuminate\Events\Dispatcher $events */ public function subscribe($events) { $events->listen( 'Illuminate\Auth\Events\Login', 'App\Listeners\UserEventSubscriber@onUserLogin' ); $events->listen( 'Illuminate\Auth\Events\Logout', 'App\Listeners\UserEventSubscriber@onUserLogout' ); } }##註冊事件訂閱者在寫完訂閱者之後,就可以透過事件分發器對訂閱者進行註冊。你可以在
EventServiceProvider 中的
$subscribe 屬性中註冊訂閱者。例如,讓我們將
UserEventSubscriber 加入到陣列列表中:
<?php namespace App\Providers; use Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvider; class EventServiceProvider extends ServiceProvider{ /** * 应用中事件监听器的映射。 * * @var array */ protected $listen = [ // ]; /** * 需要注册的订阅者类。 * * @var array */ protected $subscribe = [ 'App\Listeners\UserEventSubscriber', ]; }本文章首發在