所有支持的模型事件
在 Eloquent 模型類上進行查詢、插入、更新、刪除操作時,會觸發相應的模型事件,不管你有沒有監聽它們。這些事件包括:
retrieved | 獲取到模型實例后觸發 |
creating | 插入到數據庫前觸發 |
created | 插入到數據庫后觸發 |
updating | 更新到數據庫前觸發 |
updated | 更新到數據庫后觸發 |
saving | 保存到數據庫前觸發(插入/更新之前,無論插入還是更新都會觸發) |
saved | 保存到數據庫后觸發(插入/更新之后,無論插入還是更新都會觸發) |
deleting | 從數據庫刪除記錄前觸發 |
deleted | 從數據庫刪除記錄后觸發 |
restoring | 恢復軟刪除記錄前觸發 |
restored | 恢復軟刪除記錄后觸發 |
注:批量更新時不會觸發相應事件,因為是直接走查詢構建器完成的,繞過了模型方法。
通過監聽這些事件,我們可以在 Eloquent 模型實例生命周期的特定階段執行特定操作。在 Laravel 中我們有多種方式來監聽模型事件。
通過靜態方法監聽模型事件
通過在模型類上調用要監聽事件對應的靜態方法,一般我們會在某個服務提供者的 boot
方法中完成這項工作,比如 EventServiceProvider
。舉個例子,假設我們要監聽每次獲取模型實例的事件並在日志中記錄查詢到的用戶信息,可以這么做:
// app/Providers/EventServiceProvider.php public function boot() { parent::boot(); // 監聽模型獲取事件 User::retrieved(function ($user) { Log::info('從模型中獲取用戶[' . $user->id . ']:' . $user->name); }); }
上面這段代碼中表示我們在 User
模型上監聽 retrieved
事件,然后通過一個閉包函數執行對應的處理邏輯,該閉包函數傳入參數是模型實例,在處理邏輯中,我們通過 Log
門面記錄日志信息。
通過訂閱者監聽模型事件
①先創建對應的事件類
以刪除模型為例進行演示,分別定義一個刪除前事件類和刪除后事件類。我們通過 Artisan 命令來完成事件類初始化:
php artisan make:event UserDeleting php artisan make:event UserDeleted
然后在這兩個事件類中都添加 $user
屬性並在構造函數中傳入:
// app/Events/UserDeleted.php // app/Events/UserDeleting.php public $user; public function __construct(User $user) { $this->user = $user; }
②建立模型事件與自定義事件類的映射
在 User
模型類中建立模型事件與自定義事件類的映射,這可以通過 $dispatchesEvents
屬性來完成:
//建立模型事件與自定義事件類的映射 protected $dispatchesEvents = [ 'deleting' => UserDeleting::class, 'deleted' => UserDeleted::class ];
這樣,當我們觸發 deleting
和 deleted
事件時,底層會將其轉化為觸發 UserDeleting
和 UserDeleted
事件。
③創建訂閱者監聽事件類
我們還要監聽上述自定義的事件類,我們可以通過在 EventServiceProvider
的 listen
屬性中為每個事件綁定對應的監聽器類,也可以通過為某個模型類創建一個事件訂閱者類來統一處理該模型中的所有事件。在 app/Listeners
目錄下創建一個 UserEventSubscriber.php
文件作為訂閱者類,編寫代碼如下
<?php namespace App\Listeners; use App\Events\UserDeleted; use App\Events\UserDeleting; use Illuminate\Support\Facades\Log; class UserEventSubscriber { /** * 處理用戶刪除前事件 */ public function onUserDeleting($event) { Log::info('用戶即將刪除[' . $event->user->id . ']:' . $event->user->name); } /** * 處理用戶刪除后事件 */ public function onUserDeleted($event) { Log::info('用戶已經刪除[' . $event->user->id . ']:' . $event->user->name); } /** * 為訂閱者注冊監聽器 * * @param Illuminate\Events\Dispatcher $events */ public function subscribe($events) { $events->listen( UserDeleting::class, UserEventSubscriber::class . '@onUserDeleting' ); $events->listen( UserDeleted::class, UserEventSubscriber::class . '@onUserDeleted' ); } }
④在 EventServiceProvider
中注冊這個訂閱者
// app/Providers/EventServiceProvider.php protected $subscribe = [ UserEventSubscriber::class ];
通過觀察者監聽模型事件
針對模型事件這種特殊的事件類型,Laravel 還為我們提供了觀察者類來處理模型事件的監聽。觀察者可以看作是上述訂閱者處理模型事件的簡化版本,我們不需要自定義事件類,不需要建立映射關系,只需要在觀察者類中將需要監聽的事件定義為同名方法,並在相應方法中編寫業務處理代碼即可。當某個模型事件觸發時,Eloquent 底層會去該模型上注冊的觀察者類中通過反射查找是否定義了對應的方法,如果定義了則執行相應的邏輯,否則忽略。
下面以 saving
和 saved
事件為例演示如何通過觀察者監聽模型事件。
①通過 Artisan 命令創建針對 User
模型的觀察者
laravel5.7以上可以使用以下命令進行創建觀察者,laravel5.7以下需要手動創建
php artisan make:observer UserObserver --model=Model/User
默認生成的 UserObserver
會為 created
、 updated
、deleted
、restored
、forceDeleted
(強制刪除) 事件定義一個空方法:
<?php namespace App\Observers; use App\User; class UserObserver { /** * Handle the user "created" event. * * @param \App\User $user * @return void */ public function created(User $user) { // } /** * Handle the user "updated" event. * * @param \App\User $user * @return void */ public function updated(User $user) { // } /** * Handle the user "deleted" event. * * @param \App\User $user * @return void */ public function deleted(User $user) { // } /** * Handle the user "restored" event. * * @param \App\User $user * @return void */ public function restored(User $user) { // } /** * Handle the user "force deleted" event. * * @param \App\User $user * @return void */ public function forceDeleted(User $user) { // } }
可以把前面定義的 retrived
、deleting
、deleted
事件監聽代碼遷移過來,也可以將不需監聽的事件方法移除,這里我們將編寫保存模型時涉及的模型事件,包括 saving
、creating
、updating
、updated
、created
、saved
<?php namespace App\Observers; use App\Model\User; use Log; class UserObserver { public function saving(User $user) { Log::info('即將保存用戶到數據庫[' . $user->id . ']' . $user->name); } public function creating(User $user) { Log::info('即將插入用戶到數據庫[' . $user->id . ']' . $user->name); } public function updating(User $user) { Log::info('即將更新用戶到數據庫[' . $user->id . ']' . $user->name); } public function updated(User $user) { Log::info('已經更新用戶到數據庫[' . $user->id . ']' . $user->name); } public function created(User $user) { Log::info('已經插入用戶到數據庫[' . $user->id . ']' . $user->name); } public function saved(User $user) { Log::info('已經保存用戶到數據庫[' . $user->id . ']' . $user->name); } }
②注冊相應觀察者
編寫好觀察者后,需要將其注冊到 User
模型上才能生效,我們可以在 EventServiceProvider
的 boot
方法中完成該工作:
public function boot() { parent::boot(); //注冊User模型的觀察者 User::observe(UserObserver::class); }
結語
關於三種監聽 Eloquent 模型事件的方式,如何選擇,視情況而定。如果只是監聽一兩個模型事件,第一種方式比較合適;如果僅僅監聽系統支持的模型事件,並且要監聽多個模型的多個事件,觀察者是最佳選擇;如果還要在模型類上監聽更多系統模型事件之外的自定義事件,則使用訂閱者來監聽比較合適。