# 事件
- [簡介](#introduction)
- [注冊事件與監聽器](#registering-events-and-listeners)
- [生成事件與監聽器](#generating-events-and-listeners)
- [手動注冊事件](#manually-registering-events)
- [定義事件](#defining-events)
- [定義監聽器](#defining-listeners)
- [隊列化的事件監聽器](#queued-event-listeners)
- [手動訪問隊列](#manually-accessing-the-queue)
- [觸發事件](#firing-events)
- [事件訂閱者](#event-subscribers)
- [編寫事件訂閱者](#writing-event-subscribers)
- [注冊事件訂閱者](#registering-event-subscribers)
<a name="introduction"></a>
## 簡介
Laravel 事件機制實現了一個簡單的觀察者模式,來訂閱和監聽在應用中出現的各種事件,事件類通常被保存在 `app/Events` 目錄下,而它們的監聽器被保存在 `app/Listeners` 目錄下。如果你在應用中看不到這些文件夾也不要擔心,當你用 Artisan 命令來生成事件和監聽器的時候他們就會被創建了。
事件機制是一種很好的應用解耦方式,因為一個事件可以擁有多個互不依賴的監聽器。舉例來說,每次你把用戶的訂單發完貨后都會希望給他發個 Slack 通知。這時候你就可以發起一個 `OrderShipped` 事件,它會被對應的監聽器接收到再傳遞給 Slack 通知模塊,這樣你就不用把訂單處理的代碼跟 Slack 通知的代碼耦合在一起了。
<a name="registering-events-and-listeners"></a>
## 注冊事件和監聽器
包含在你 Laravel 應用中的 `EventServiceProvider` 提供了一個很方便的地方來注冊所有的事件監聽器。`listen` 屬性是一個數組,它包含了所有事件(鍵)以及事件對應的監聽器(值)。你也可以根據應用需求來增加事件到這個數組,例如,我們增加一個 `OrderShipped` 事件:
/**
* 應用程序的事件監聽器映射。
*
* @var array
*/
protected $listen = [
'App\Events\OrderShipped' => [
'App\Listeners\SendShipmentNotification',
],
];
<a name="generating-events-and-listeners"></a>
### 生成事件和監聽器
手動創建事件和監聽器是很麻煩的,簡單的方式是,在 `EventServiceProvider` 中寫上事件和監聽器然后使用 `event:generate` 命令。這個命令會自動生成在 `EventServiceProvider` 中列出的所有事件和監聽器,當然已經存在的事件和監聽器將保持不變:
php artisan event:generate
<a name="manually-registering-events"></a>
### 手動注冊事件
一般來說,事件必須通過 `EventServiceProvider` 的 `$listen` 數組進行注冊;不過,你也可以通過 `Event` facade 或是 `Illuminate\Contracts\Events\Dispatcher` contract 實現的事件發送器來手動注冊事件:
/**
* 注冊你應用程序中的任何其它事件。
*
* @param \Illuminate\Contracts\Events\Dispatcher $events
* @return void
*/
public function boot(DispatcherContract $events)
{
parent::boot($events);
$events->listen('event.name', function ($foo, $bar) {
//
});
}
<a name="defining-events"></a>
## 定義事件
一個事件類是包含了相關事件信息的數據容器。例如,假設我們生成的 `OrderShipped` 事件接收一個 [Eloquent ORM](/docs/{{version}}/eloquent) 對象:
<?php
namespace App\Events;
use App\Order;
use App\Events\Event;
use Illuminate\Queue\SerializesModels;
class OrderShipped extends Event
{
use SerializesModels;
public $order;
/**
* 創建一個事件實例
*
* @param Order $order
* @return void
*/
public function __construct(Order $order)
{
$this->order = $order;
}
}
正如你所見的,這個事件類沒有包含其它邏輯。它只是一個被購買的 `Order` 對象的容器。如果事件對象是使用 PHP 的 `serialized` 函數進行序列化,那么事件所使用的 `SerializesModels` trait 將會優雅的序列化任何的 Eloquent 模型。
<a name="defining-listeners"></a>
## 定義監聽器
接下來,讓我們看一下例子中事件的監聽器。事件監聽器的 `handle` 方法接收了事件實例。`event:generate` 命令將會在事件的 `handle` 方法中自動加載正確的事件類和類型提示。在 `handle` 方法內,你可以運行任何需要響應該事件的業務邏輯。
<?php
namespace App\Listeners;
use App\Events\OrderShipped;
class SendShipmentNotification
{
/**
* 創建事件監聽器。
*
* @return void
*/
public function __construct()
{
//
}
/**
* 處理事件。
*
* @param OrderShipped $event
* @return void
*/
public function handle(OrderShipped $event)
{
// 使用 $event->order 來訪問 order ...
}
}
> {tip} 你的事件監聽器也可以在構造器內對任何依賴使用類型提示。所有事件監聽器經由 Laravel [服務容器](/docs/{{version}}/container) 做解析,所以依賴將會被自動注入:
#### 停止事件傳播
有時候,你可能希望停止一個事件傳播到其它的監聽器。你可以通過在偵聽器的 `handle` 方法中返回 `false` 來實現。
<a name="queued-event-listeners"></a>
### 隊列化的事件監聽器
如果你對監聽器要實現耗時任務比如發郵件或者進行 HTTP 請求,那把它放到隊列中處理是有好處的。在使用隊列化監聽器之前,一定要在服務器或者本地開發環境中 [配置隊列](/docs/{{version}}/queues) 并且開啟一個隊列監聽器。
要指定一個監聽器應該隊列化的話,增加 `ShouldQueue` 接口到你的監聽器類就好了。由 `event:generate` Artisan 命令生成的偵聽器已經將此接口導入到命名空間了,因此可以像這樣來立即使用它:
<?php
namespace App\Listeners;
use App\Events\OrderShipped;
use Illuminate\Contracts\Queue\ShouldQueue;
class SendShipmentNotification implements ShouldQueue
{
//
}
就這樣!現在,當這個監聽器被調用時,事件分發器會使用 Laravel 的 [隊列系統](/docs/{{version}}/queues) 自動將它進行隊列化。如果監聽器通過隊列運行而沒有拋出任何異常,則已執行完的任務將會自動從隊列中被刪除。
<a name="manually-accessing-the-queue"></a>
### 手動訪問隊列
如果你需要手動訪問底層隊列任務的 `delete` 和 `release` 方法,你可以使用 `Illuminate\Queue\InteractsWithQueue` trait 來實現。這個 trait 在生成的監聽器中是默認加載的,它提供了這些方法:
<?php
namespace App\Listeners;
use App\Events\OrderShipped;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Contracts\Queue\ShouldQueue;
class SendShipmentNotification implements ShouldQueue
{
use InteractsWithQueue;
public function handle(OrderShipped $event)
{
if (true) {
$this->release(30);
}
}
}
<a name="firing-events"></a>
## 觸發事件
如果要觸發一個事件,你可以發送一個事件的實例到 `event` 輔助函數。這個函數將會把事件分發到它所有已經注冊的監聽器上。因為 `event` 函數是全局可訪問的,所以你可以在應用中任何地方調用:
<?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 [內置的測試輔助方法](/docs/{{version}}/mocking#mocking-events) 讓這件事變得很容易。
<a name="event-subscribers"></a>
## 事件訂閱者
<a name="writing-event-subscribers"></a>
### 編寫事件訂閱者
事件訂閱者是一個在自身內部可以訂閱多個事件的類,允許你在單個類內定義多個事件的處理器。訂閱者應該定義一個 `subscribe` 方法,這個方法將被傳遞過來一個事件分發器的實例。你可以調用事件分發器的 `listen` 方法來注冊事件監聽器:
<?php
namespace App\Listeners;
class UserEventListener
{
/**
* 處理用戶登錄事件。
*/
public function onUserLogin($event) {}
/**
* 處理用戶注銷事件。
*/
public function onUserLogout($event) {}
/**
* 為訂閱者注冊監聽器。
*
* @param Illuminate\Events\Dispatcher $events
*/
public function subscribe($events)
{
$events->listen(
'App\Events\UserLoggedIn',
'App\Listeners\UserEventListener@onUserLogin'
);
$events->listen(
'App\Events\UserLoggedOut',
'App\Listeners\UserEventListener@onUserLogout'
);
}
}
<a name="registering-event-subscribers"></a>
### 注冊事件訂閱者
一旦訂閱者被定義,它就可以被注冊到事件分發器中。你可以在 `EventServiceProvider` 中使用 `$subscribe` 屬性注冊訂閱者。例如,我們增加 `UserEventListener` 到列表中:
<?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\UserEventListener',
];
}
- 說明
- 翻譯說明
- 發行說明
- 升級說明
- 貢獻導引
- 入門指南
- 安裝
- 配置信息
- 文件夾結構
- 錯誤與日志
- 開發環境
- HomeStead
- Valet
- 核心概念
- 服務容器
- 服務提供者
- 門面(facades)
- contracts
- HTTP層
- 路由
- 中間件
- CSRF保護
- 控制器
- 請求
- 響應
- Session
- 表單驗證
- 視圖與模板
- 視圖
- Blade模板
- 本地化
- Javascript與CSS
- 入門指南
- laravel-elixir
- 安全
- 用戶認證
- 用戶授權
- 重置密碼
- API授權
- 加密解密
- 哈希
- 綜合話題
- 廣播系統
- 緩存系統
- 事件系統
- 文件存儲
- 郵件發送
- 消息通知
- 隊列
- 數據庫
- 快速入門
- 查詢構造器
- 分頁
- 數據庫遷移
- 數據填充
- redis
- Eloquent ORM
- 快速入門
- 模型關聯
- Eloquent集合
- 修改器
- 序列化
- Artisan控制臺
- Artisan 命令行
- 任務調度
- 測試
- 快速入門
- 應用程序測試
- 數據庫測試
- 模擬器
- 官方擴展包
- Cashier交易包
- Envoy 部署工具
- Passport OAuth 認證
- Scout 全文搜索
- Socialite 社交化登錄
- 附錄
- 集合
- 輔助函數
- 擴展包開發
- 交流說明