巧用 Laravel 模型事件

file

Laravel模型事件容許你利用模型生命週期中的各個點,甚至能夠防止發生保存或刪除。 Laravel 模型事件文檔 概述瞭如何使用事件類來引入這些事件,但本文旨在創建並補充關於設置事件和監聽者的一些額外細節。php

事件概覽

Eloquent 有許多事件,你能夠將其添加到模型中並添加自定義功能。到目前爲止,模型支持的事件以下:laravel

  • retrieved
  • creating
  • created
  • updating
  • updated
  • saving
  • saved
  • deleting
  • deleted
  • restoring
  • restored

從官方文檔中能夠看到它們的工做原理,而且你能夠跳到基本的Model類中,看看它們是如何工做的:git

當在數據庫中一個模型 Model 被檢索時,retrieved 事件將觸發。 新模型第一次保存時,creatingcreated 事件將觸發。 若是模型已經存在於數據庫中而且調用了 save 方法,則 updating/ updated 事件將觸發。 可是,在這兩種狀況下,saving / saved 事件都會觸發。github

該文檔提供了一個很好的概述,並解釋瞭如何觸發這些事件,但若是你是新手或不熟悉如何將事件監聽器鏈接到這些自定義模型事件,請進一步閱讀。數據庫

註冊事件

爲了在模型中關聯事件,首先須要使用 $dispatchesEvents 屬性來註冊事件對象,事件對象最終將經過 HasEvents::fireCustomModelEvent() 方法觸發,該方法經過 fireModelEvent() 調用。fireCustomModelEvent() 方法以下所示:bash

/**
 * Fire a custom model event for the given event.
 *
 * @param  string  $event
 * @param  string  $method
 * @return mixed|null
 */
protected function fireCustomModelEvent($event, $method)
{
    if (! isset($this->dispatchesEvents[$event])) {
        return;
    }

    $result = static::$dispatcher->$method(new $this->dispatchesEvents[$event]($this));

    if (! is_null($result)) {
        return $result;
    }
}

某些事件(如 delete)將檢查事件是否返回 false,而後退出操做。例如,你能夠使用此hook進行一些檢查並防止建立或刪除用戶。app

App\User 模型爲例,配置模型事件:框架

protected $dispatchesEvents = [
    'saving' => \App\Events\UserSaving::class,
];

你能夠使用 php artisan make:event 命令來建立此事件,UserSaving 類的完整代碼以下:ide

<?php

namespace App\Events;

use App\User;
use Illuminate\Queue\SerializesModels;

class UserSaving
{
    use SerializesModels;

    public $user;

    /**
     * Create a new event instance.
     *
     * @param \App\User $user
     */
    public function __construct(User $user)
    {
        $this->user = $user;
    }
}

咱們的事件類提供了一個公共 $user 屬性,所以你能夠在保存事件期間訪問用戶模型實例。測試

下面咱們爲事件設置一個實際的監聽者。當用戶模型觸發 saving 事件時,監聽者被調用,從而容許咱們在事件期間使用模型。

建立一個事件監聽器

如今咱們的自定義 User 模型事件在保存期間觸發,咱們須要爲它註冊一個監聽者。咱們將在一秒鐘內使用模型觀察者,但我先來看下爲單個事件配置事件(Event)和監聽列表。

事件監聽器就像任何其餘Laravel事件監聽器同樣,handle() 方法將接受 App\Events\UserSaving 事件類的一個實例。

你能夠手工建立它,或者使用 php artisan make:listener 命令。可是,你也能夠這樣建立它,下面是一個建立好的監聽者類:

<?php

namespace App\Listeners;

use App\Events\UserSaving as UserSavingEvent;

class UserSaving
{
    /**
     * Handle the event.
     *
     * @param  \App\Events\UserSavingEvent $event
     * @return mixed
     */
    public function handle(UserSavingEvent $event)
    {
        app('log')->info($event->user);
    }
}

綁定事件和監聽者

上面只是給 app('log')->info($event->user); 添加了一個log調用,以便如今能夠檢查傳遞給偵聽器的模型。爲此,咱們須要在 EventServiceProvider::$listen 屬性中註冊監聽器:

<?php

namespace App\Providers;

use Illuminate\Support\Facades\Event;
use Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvider;

class EventServiceProvider extends ServiceProvider
{
    /**
     * The event listener mappings for the application.
     *
     * @var array
     */
    protected $listen = [
        \App\Events\UserSaving::class => [
            \App\Listeners\UserSaving::class,
        ],
    ];

    // ...
}

如今,當模型調度事件時,咱們已註冊的偵聽器,就能夠在 saving 事件期間被調用。

試用事件處理程序

咱們能夠使用 tinker 來快速嘗試咱們的事件監聽器:

php artisan tinker
>>> factory(\App\User::class)->create();
=> App\User {#794
     name: "phpcasts",
     email: "model_event@phpcasts.org",
     updated_at: "2018-05-21 13:57:18",
     created_at: "2018-05-21 13:57:18",
     id: 2,
   }

若是你已正確註冊了事件和偵聽者,則應該在 storage/logs/laravel.log 文件中看到該模型的JSON表示形式:

[2018-05-17 13:57:18] local.INFO: {"name":"phpcasts","email":"model_event@phpcasts.org"}

請注意,此時模型沒有 created_atupdated_at 屬性。 若是你在模型上再次調用 save(),則日誌將具備包含時間戳的新記錄,由於 saving 事件會在新建立的記錄和現有記錄上觸發:

>>> $u = factory(\App\User::class)->create();
=> App\User {#741
     name: "phpcasts",
     email: "model_event@phpcasts.org",
     updated_at: "2018-05-21 13:59:37",
     created_at: "2018-05-21 13:59:37",
     id: 3,
   }
>>> $u->save();
=> true
>>>

中止保存

一些模型事件容許你阻止繼續操做。在咱們這個測試的例子中,假設咱們不但願在 $user->name 屬性中保存包含 adminroot 等名稱的用戶的模型,則能夠這樣作:

/**
 * Handle the event.
 *
 * @param  \App\Events\UserSaving $event
 * @return mixed
 */
public function handle(UserSaving $event)
{
    if (in_array($event->user->name, ['admin', 'root'])) {
        return false;
    }
}

在基於 Eloquent Model::save() 的方法中,是否會根據此事件處理程序的結果中止保存的事件檢查代碼:

public function save(array $options = [])
{
    $query = $this->newQueryWithoutScopes();

    // If the "saving" event returns false we'll bail out of the save and return
    // false, indicating that the save failed. This provides a chance for any
    // listeners to cancel save operations if validations fail or whatever.
    if ($this->fireModelEvent('saving') === false) {
        return false;
    }

save() 方法對於自定義事件如何進入模型生命週期是個比較好的例子,而且能夠被動地執行日誌數據或派發做業等任務。

使用觀察者

若是你正在監聽多個事件,則可能會發現使用觀察者類將一個類中的多個事件分組更加的方便。如下是來自 Eloquent事件文檔 的一個例子:

<?php

namespace App\Observers;

use App\User;

class UserObserver
{
    /**
     * Listen to the User created event.
     *
     * @param  \App\User  $user
     * @return void
     */
    public function created(User $user)
    {
        //
    }

    /**
     * Listen to the User deleting event.
     *
     * @param  \App\User  $user
     * @return void
     */
    public function deleting(User $user)
    {
        //
    }
}

能夠在服務提供者 AppServiceProviderboot() 方法中註冊觀察者 :

/**
 * Bootstrap any application services.
 *
 * @return void
 */
public function boot()
{
    User::observe(UserObserver::class);
}

總結

事件/監聽者 和 觀察者均可以達到監聽模型事件的做用,體會二者的使用。

更多

能夠閱讀 Laravel事件文檔,以更多地瞭解事件和監聽者如何在整個框架中工做。 Eloquent事件文檔 是可用事件以及如何使用觀察者的很好參考。 最後,我建議經過瀏覽 Illuminate\Database\Eloquent\Model 類來查找fireModelEvent() 方法調用的用法,以查看事件如何與模型以及將這些事件組合在一塊兒的 HasEvent trait。

更多PHP知識,能夠前往 PHPCasts

相關文章
相關標籤/搜索