Laravel 模型事件入門

Laravel 模型事件容許你監聽模型生命週期內的多個關鍵點,甚至能夠在阻止一個模型的保存或者刪除。 Laravel 模型事件文檔 概述瞭如何使用鉤子將對應事件與相關的事件類型關聯起來,可是本文的主旨是事件與監聽器的構建與設置,並額外補充一些細節的說明。php

事件概述

Eloquent 有不少事件可讓你使用鉤子將它們關聯起來,而且增長自定義的功能到你的模型中。該模型起始時有如下事件:laravel

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

從文檔這裏咱們能夠了解它們都是如何實現的,你還能夠進入 Model 的基類去看看它們究竟是如何實現的:git

當現有模型被數據庫檢索時, retrieved 事件將會觸發。當一個新的模型被第一次保存時,  creating 和 created 事件將會觸發。若是對一個已經存在於數據庫的模型調用 save 方法, updating / updated 事件將會觸發。不管怎樣,在這兩種狀況下, saving / saved 事件都會觸發。github

文檔中對模型事件進行了很好的概述,同時解釋了怎樣使用鉤子去關聯事件,可是若是你是初學者,或者並非熟悉怎樣使用鉤子將事件監聽器與這些自定義模型事件相關聯,請進一步閱讀本文。數據庫

註冊 事件

爲了在你的模型中關聯一個事件,你須要作的第一件事是使用 $dispatchesEvents 屬性去註冊事件對象,這最終將經過  HasEvents::fireCustomModelEvent() 方法觸發, 該方法將經過  fireModelEvent() 方法被調用。 fireCustomModelEvent() 方法原始的時候大體是下面這樣:app

/**
 * 爲給定的事件觸發一個自定義模型。
 *
 * @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 而後退出操做。好比,你可使用這個鉤子去作一些檢測,也能夠防止一個用戶被建立或刪除。框架

使用  App\User 模型舉例, 這裏展現瞭如何配置你的模型事件:ide

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

你可使用 artisan make:event 命令來爲你建立這個事件, 但基本上這將是你最後獲得結果 :this

<?php

namespace App\Events;

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

class UserSaving
{
    use SerializesModels;

    public $user;

    /**
     *  建立一個新的事件實例
     *
     * @param \App\User $user
     */
    public function __construct(User $user)
    {
        $this->user = $user;
    }
}

咱們的事件提供了一個公有的 $user 屬性以便你可以在 saving事件期間訪問 User模型實例。spa

爲了讓它工做起來下一步須要作的是爲這個事件創建一個實際的監聽器。咱們設置好模型的觸發時機,當 User模型觸發 saving 事件,監聽器就會被調。

建立一個事件監聽器

如今,咱們定義 User 模型並註冊一個事件監聽器來監聽 saving 事件的觸發。雖然,我能經過模型觀察器快速實現,可是,我想引導你爲單個事件觸發配置事件監聽器。

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

你能夠手動建立它,也可使用 php artisan make:listener 命令。 無論怎麼樣,你都將建立一個像下面這樣子監聽類:

<?php

namespace App\Listeners;

use App\Events\UserSaving as UserSavingEvent;

class UserSaving
{
    /**
     * 處理事件。
     *
     * @param  \App\Events\UserSavingEvent $event
     * @return mixed
     */
    public function handle(UserSavingEvent $event)
    {
        app('log')->info($event->user);
    }
}

我只是添加了一個日誌記錄調用,以便於檢查傳遞給監聽器的模型。爲此,咱們還須要在 EventServiceProvider::$listen 屬性中註冊監聽器:

<?php

namespace App\Providers;

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

class EventServiceProvider extends ServiceProvider
{
    /**
     * 應用的事件監聽器。
     * 
     * @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: "Aiden Cremin",
     email: "josie05@example.com",
     updated_at: "2018-03-15 03:57:18",
     created_at: "2018-03-15 03:57:18",
     id: 2,
   }

若是你已正確註冊了事件和監聽器,則應該在  laravel.log 文件中能夠看到該模型的 JSON 表達形式:

[2018-03-15 03:57:18] local.INFO: {"name":"Aiden Cremin","email":"josie05@example.com"}

要注意的一點,此時模型並無 created_at 或 updated_at 屬性。若是在模型上再次調用 save() ,日誌上將會有一個帶有時間戳的新記錄,由於 saving 事件會在新建立的記錄或如今有記錄上觸發:

>>> $u = factory(\App\User::class)->create();
=> App\User {#741
     name: "Eloisa Hirthe",
     email: "gottlieb.itzel@example.com",
     updated_at: "2018-03-15 03:59:37",
     created_at: "2018-03-15 03:59:37",
     id: 3,
   }
>>> $u->save();
=> true
>>>

中止一個保存操做

某些模型事件是容許你進行阻止操做的。舉個荒謬的例子,假設咱們不容許任何一個用戶的模型保存其屬性 $user->name  的內容爲 Paul

/**
 * 處理事件。
 *
 * @param  \App\Events\UserSaving $event
 * @return mixed
 */
public function handle(UserSaving $event)
{
    if (stripos($event->user->name, 'paul') !== false) {
        return false;
    }
}

在 Eloquent 的 Model::save() 方法中,會根據事件監聽的返回結果判斷是否進行中止保存操做:

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

    // 若是 "saving" 事件返回 false ,咱們將退出保存並返回
    // false,表示保存失敗。這爲服務監聽者提供了一個機會,
    // 當驗證失敗或者出現其它任何狀況,均可以取消保存操做。
    if ($this->fireModelEvent('saving') === false) {
        return false;
    }

這個  save()  是個很好的例子,它告訴了你如何在模型生命週期中自定義事件,以及被動執行日誌數據記錄或者任務調度。

使用觀察者

若是你正在監聽多個事件,那麼你可能會發現使用觀察者類來按類型分組存放事件會更加方便。這裏是一個例子 Eloquent 觀察者

<?php

namespace App\Observers;

use App\User;

class UserObserver
{
    /**
     * 監聽 User 建立事件。
     *
     * @param  \App\User  $user
     * @return void
     */
    public function created(User $user)
    {
        //
    }

    /**
     * 監聽 User 刪除事件。
     *
     * @param  \App\User  $user
     * @return void
     */
    public function deleting(User $user)
    {
        //
    }
}

你能夠在服務提供者 AppServiceProvider 中的 boot() 方法裏註冊觀察者。

/**
 * 運行全部應用服務。
 *
 * @return void
 */
public function boot()
{
    User::observe(UserObserver::class);
}

瞭解更多

我建議你閱讀 Laravel 事件文檔 去了解有關事件和監聽器在整個框架中如何工做。 Eloquent 事件文檔  對於可用事件以及如何使用觀察者是一個很是好的參考。最後,我建議瀏覽 Illuminate\Database\Eloquent\Model 類來查找 fireModelEvent() 方法調用的用法去了解事件如何與模型和 HasEvents trait 將這些事件聯繫在一塊兒。

現代化 PHP 知識突飛猛進,尤爲是 PHP7 出來之後,歡迎加入 PHP / Laravel 知識社區 一塊兒成長。

相關文章
相關標籤/搜索