laravel 的事件提供了一種簡單的觀察者實現。它允許你在應用中進行訂閱和監聽事件。事件類通常都是存儲在 app/Events 目錄中,而他們的監聽者都是存儲在 app/Listeners 目錄中。
注冊事件/監聽者
EventServiceProvider 提供了一個注冊所有事件監聽者的方便的場所。它的 listen 屬性包含了一個所有事件(keys)以及他們的監聽者(values)所組成的數組。當然,你可以在這個數組中添加任何你需要的事件。比如,讓我們添加 PodcastWasPurchased 事件:
/** * The event listener mappiings for the application. * * @var array */ protected $listen = [ 'App\Events\PodcastWasPurchased' => [ 'App\Listeners\EmailPurchaseConfirmation', ], ],
生成事件/監聽者類
當然,每次都手動的去創建一個事件文件和一個監聽者文件是很麻煩的事情,所以,你可以在 EventServiceProvider 中添加事件和監聽者然后使用 event:generate 命令來自動生成。這個命令會生成 EventServiceProvider 中的所有列出的事件和監聽者,當然,已經存在的事件和監聽者不會重新生成:
php artisan event:generate
手動的注冊事件
事件應該被注冊在 EventServiceProvider 的 $listen 數組中
<?php
namespace App\Providers;
use App\Listeners\BaseLogEventSubscriber;
use Laravel\Lumen\Providers\EventServiceProvider as ServiceProvider;
class EventServiceProvider extends ServiceProvider
{
/**
* The event listener mappings for the application.
*
* @var array
*/
protected $listen = [
'App\Events\ExampleEvent' => [
'App\Listeners\ExampleListener',
],
'App\Events\UpdateSpuInfoEvent' => [
'App\Listeners\UpdateSpuInfoListener',
],
'App\Events\UpdateProductSearchIndexesEvent' => [
'App\Listeners\UpdateProductSearchIndexesListener',
],
'App\Events\UpdateSpuStatusEvent' => [
'App\Listeners\UpdateSpuStatusListener',
],
];
protected $subscribe = [
BaseLogEventSubscriber::class,
];
}
事件監聽通配符
你可以在注冊監聽器的時候使用 * 來作為通配符。這允許你來在同一個監聽器中監聽多種事件。通配符監聽器接收整個事件數據數組作為第一個參數:
$events->listen('event.*', function (array $data) { // });
定義事件
一個事件類只是簡單的數據存儲器,它應該持有事件相關的信息.比如,讓我們假設我們生成的 PodcastWasPurchased 事件應該接收一個 Eloquent ORM 對象:
<?php namespace App\Events; use App\podcast; use App\Events\Event; use Illuminate\Queue\SerializesModels; class PodcastWasPurchased extends Event { use SerializesModels; public $podcast; /** * Create a new event instance. * * @param Podcast $podcast * @return void */ public function __construct(Podcast $podcast) { $this->podcast = $podcast; } }
就如你所看到的,這個事件類並沒有包含什么業務邏輯。它只是簡單的包含了一個被購買的 Podcast 對象。SerializesModels trait 被用來序列化 Eloquent 模型,如果事件對象是使用 PHP 的 serialize 方法被進行序列化,那么 SerializesModels 就會優雅的將其內部的 Eloquent 對象序列化。
定義監聽者
接着,讓我們來看一下針對我們上面舉例的事件的監聽者。事件監聽者會在其 handle 方法中接收事件的實例。event:generate 命令會自動的在 handle 方法中引入其對應的事件的類型提示。你可以在 handle 方法中來提供對事件的響應邏輯:
<?php namespace App\Listeners; use App\Events\PodcastWasPurchased; class EmailPurchaseConfirmation { /** * Create the event listener. * * @return void */ public function __construct() { // } /** * Handle the event. * * @param PodcastWasPurchased $event * @return void */ public function handle(PodcastWasPurchased $event) { // Access the podcast using $event->podcast... } }
你的事件監聽者也可以在構造函數中進行類型提示來注入依賴。所有的事件監聽器都是通過 laravel 的服務容器解析的。所以,它們的依賴可以被自動的注入:
use Illuminate\Contracts\Mail\Mailer; public function __construct(Mailer $mailer) { $this->mailer = $mailer; }
停止傳遞事件
有時候,你可能希望停止傳遞事件到后續的監聽者中。你可以在監聽者的 handle 方法中返回 false,這樣后續的監聽者將不再進行事件的響應。
監聽者隊列化
需要對事件監聽者進行隊列化?沒有比這更簡單的了。直接添加 ShouldeQueue 接口到監聽者類中就可以了。通過 event:generate Artisan 命令生成的監聽者已經在當前的命名空間中添加了對這個接口的支持,所以你立即就可以使用:
<?php namespace App\Listeners; use App\Events\PodcastWasPurchased; use Illuminate\Contracts\Queue\ShouldQueue; class EmailPurchaseConfirmation implements ShouldQueue {
/**
* 任務應該發送到的隊列的連接的名稱 (可以不寫,然后通過QUEUE_CONNECTION來配置)
*
* @var string|null
*/
public $connection = 'redis';
/**
* 任務應該發送到的隊列的名稱 (默認為defalut)
*
* @var string|null
*/
public $queue = 'listeners';
}
// 可以在redis中通過 redis-cli keys "queues*"來查看
就這么簡單!當事件觸發時,事件分發器會使用 laravel 的隊列系統對監聽器進行自動的隊列化調用。如果監聽器隊列化執行的過程中沒有異常出現,隊列任務會自動的在監聽器進程完成后進行刪除。
手動的訪問隊列
如果你需要手動的訪問底層隊列任務的 delete 和 release 方法。那么你就可以直接去使用它們。Illuminate\Queue\InteractsWithQueue trait 對默認生成的監聽器提供了訪問這些方法的權限:
<?php namespace App\Listeners; use App\Events\PodcastWasPurchased; use Illuminate\Queue\InteractsWithQueue; use Illuminate\Contracts\Queue\ShouldQueue; class EmailPurchaseConfirmation implements ShouldQueue { use InteractsWithQueue; public function handle(PodcastWasPurchased $event) { if (true) { $this->release(30); } } }
觸發事件
你可以使用 Event 假面來進行事件的觸發,你需要傳遞一個事件實例到 fire 方法中。fire 方法會分發事件到所有的監聽器中:
<?php namespace App\Http\Controllers; use Event; use App\Podcast; use App\Events\PodcastWasPurchased; use App\Http\Controllers\Controller; class UserController extends Controller { /** * Show the profile for the given user. * * @param int $userId * @param int $podcastId * @return Response */ public function purchasePodcast($userId, $podcastId) { $podcast = Podcast::findOrFail($podcastId); // Purchase podcast logic... Event::fire(new PodcastWasPurchased($podcast)); } }
此外,你還可以通過使用全局 event 幫助函數來觸發事件:
event( new PodcastWasPurchased($podcast));


