Laravel 中的模型事件與 Observer

模型事件

Laravel 的世界中,你對 Eloquent 大多數操做都會或多或少的觸發一些模型事件,今天就來看一下模型事件的使用。php

Laravel 事先已經定義好了 10 個模型事件以供咱們使用,它們分別是:數據庫

creating, created , updating, updated, saving, saved, deleting, deleted, restoring, restored數組

事件名稱都很淺顯易懂,相信若是你是用腦子寫代碼的話都應該能夠看明白,若是不明白的話能夠左轉百度。ide

不過你可能對 updating, updated, saving, saved 這四個事件有所疑惑,因此咱們來詳細分析一下。ui

打開 Illuminate\Database\Eloquent\Model ,找到位於 517 行的 save 方法this

筆者使用的是 Laravel 5.5 最新版本,若是你使用的是其餘版本,請自行在官方文檔 版本差別 中對比你的版本spa

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

    if ($this->fireModelEvent('saving') === false) {
        return false;
    }

    if ($this->exists) {
        $saved = $this->isDirty() ?
                    $this->performUpdate($query) : true;
    }

    else {
        $saved = $this->performInsert($query);

        if (! $this->getConnectionName() &&
            $connection = $query->getConnection()) {
            $this->setConnection($connection->getName());
        }
    }

    if ($saved) {
        $this->finishSave($options);
    }

    return $saved;
}

能夠看到首先觸發的是 savingrest

$this->fireModelEvent('saving')

接着會判斷此 model 是否是新建立的。code

若是是新建立的,那麼則會進行 insert 操做,因爲本例是分析修改操做,因此這裏直接略過。orm

若是不是新建立的,那麼會調用 isDirty 方法判斷此 model 是否進行了修改。

若是進行了修改,那麼會調用 performUpdate 方法進行更新操做,若是沒有進行修改,則直接返回 true

讓咱們點開 performUpdate 方法。

protected function performUpdate(Builder $query)
{
    if ($this->fireModelEvent('updating') === false) {
        return false;
    }

    if ($this->usesTimestamps()) {
        $this->updateTimestamps();
    }

    $dirty = $this->getDirty();

    if (count($dirty) > 0) {
        $this->setKeysForSaveQuery($query)->update($dirty);

        $this->fireModelEvent('updated', false);

        $this->syncChanges();
    }

    return true;
}

能夠看到這裏會觸發 updating

$this->fireModelEvent('updating')

若是返回的不是 false ,那麼會接着往下走,判斷一下此模型是否使用了 Timestamp,若是使用了,那麼則先更新自身的 Timestamp

if ($this->usesTimestamps()) {
    $this->updateTimestamps();
}

接着調用 getDirty,顧名思義,大概能夠知道這個方法是獲取自身的髒值,讓咱們點開看一下:

public function getDirty()
{
    $dirty = [];

    foreach ($this->getAttributes() as $key => $value) {
        if (! $this->originalIsEquivalent($key, $value)) {
            $dirty[$key] = $value;
        }
    }

    return $dirty;
}

意料之中,此方法內會返回自身被修改後的髒值,若是你打印過 LaravelModel 實例,那麼相信你會看到實例中有 originalattributes 兩個數組。

其中 original 保存的是最初始的實例屬性,沒法被外部直接修改。

attributes 數組則是能夠被自有修改,此方法便是由判斷兩個數組的差別而返回髒值,因爲時間緣由,這裏不作過多詳解,有機會單開一個文章慢慢講。

繼續往下走,能夠看到這裏會判斷一下是否有髒值:

if (count($dirty) > 0)

只有在存在髒值的狀況下才會進入 if 內部,執行 update 操做,繼而觸發 updated 事件

update 結束後會執行一下 syncChanges 同步一下自身的數據

OK, performUpdate 方法解析完畢,讓咱們回到 save 方法,接着往下看,能夠看到 Laravel 最後作了一個判斷:

if ($saved) {
    $this->finishSave($options);
}

只有在 update 成功的狀況下,纔會觸發 finishSave() 方法,此方法會接收一個參數 $options,便是修改的屬性。

讓咱們點開此方法:

protected function finishSave(array $options)
{
    $this->fireModelEvent('saved', false);

    if ($this->isDirty() && ($options['touch'] ?? true)) {
        $this->touchOwners();
    }

    $this->syncOriginal();
}

能夠看到在此方法內會觸發 saved 事件

分析完畢,大概能夠作一個總結了。

當模型已存在,不是新建的時候,依次觸發的順序是:

saving -> updating -> updated -> saved

當模型不存在,須要新增的時候,依次觸發的順序則是

saving -> creating -> created -> saved

那麼 saving,savedupdating,updated 到底有什麼區別呢?

上面已經講過,LaravelEloquent 會維護實例的兩個數組,分別是 originalattributes

只有在 saved 事件觸發以後,Laravel 纔會對兩個數組執行 syncOriginal 操做,這樣就很好理解了。

updatingupdated 會在數據庫中的真值修改先後觸發。

savingsaved 則會在 Eloquent 實例的 original 數組真值更改先後觸發。

這樣咱們就能夠根據業務場景來選擇更合適的觸發事件了~

Observer (觀察者)

若是你想在一個模型中監聽多個事件,那麼你能夠把它寫成一個類,類中的方法名稱便是你想要監聽的事件名稱

class UserObserver
{

    /**
     * 監聽數據即將建立的事件。
     *
     * @param  User $user
     * @return void
     */
    public function creating(User $user)
    {

    }

    /**
     * 監聽數據建立後的事件。
     *
     * @param  User $user
     * @return void
     */
    public function created(User $user)
    {

    }

    /**
     * 監聽數據即將更新的事件。
     *
     * @param  User $user
     * @return void
     */
    public function updating(User $user)
    {

    }

    /**
     * 監聽數據更新後的事件。
     *
     * @param  User $user
     * @return void
     */
    public function updated(User $user)
    {

    }

    /**
     * 監聽數據即將保存的事件。
     *
     * @param  User $user
     * @return void
     */
    public function saving(User $user)
    {

    }

    /**
     * 監聽數據保存後的事件。
     *
     * @param  User $user
     * @return void
     */
    public function saved(User $user)
    {

    }

    /**
     * 監聽數據即將刪除的事件。
     *
     * @param  User $user
     * @return void
     */
    public function deleting(User $user)
    {

    }

    /**
     * 監聽數據刪除後的事件。
     *
     * @param  User $user
     * @return void
     */
    public function deleted(User $user)
    {

    }

    /**
     * 監聽數據即將從軟刪除狀態恢復的事件。
     *
     * @param  User $user
     * @return void
     */
    public function restoring(User $user)
    {

    }

    /**
     * 監聽數據從軟刪除狀態恢復後的事件。
     *
     * @param  User $user
     * @return void
     */
    public function restored(User $user)
    {

    }
}

而後在 AppServiceProvider 中註冊此觀察者

<?php

namespace App\Providers;

use App\User;
use App\Observers\UserObserver;
use Illuminate\Support\ServiceProvider;

class AppServiceProvider extends ServiceProvider
{
    /**
     * 運行全部應用.
     *
     * @return void
     */
    public function boot()
    {
    
         // 爲 User 模型註冊觀察者
        User::observe(UserObserver::class);
    }

    /**
     * 註冊服務提供.
     *
     * @return void
     */
    public function register()
    {
        //
    }
}

而後你就能夠在你註冊的 Observer 中觀測到各類事件啦~

相關文章
相關標籤/搜索