【Laravel-海賊王系列】第十五章,Model Observer 解析

Jijilin發表於2019-03-11

簡介

可以替代資料庫的觸發器功能。

Observer 的建立

觀察者的功能來自與繼承的 Model 物件的 trait HasEvents 特質。

本章我們使用 Laravel 預設的 User 模型來進行說明。

【Laravel-海賊王系列】第十五章,Model Observer 解析

建立一個觀察者類

【Laravel-海賊王系列】第十五章,Model Observer 解析

註冊 Observer

通過 provider 來註冊 Observer

【Laravel-海賊王系列】第十五章,Model Observer 解析

關於 provider 載入機制可以移步【Laravel-海賊王系列】第八章, Provider 功能解析

觀察過程解析

User::observe() 方法來自 Illuminate\Database\Eloquent\Concerns\HasEvents;

上面介紹過了,這個特質類是關鍵,我也主要圍繞這個類來分析。

public static function observe($classes)
{
    // "指向 User 物件"
    $instance = new static;

    // "支援一次傳入陣列或者字串的型式統一包裝成陣列"
    // "這裡可以看出 observe() 方法支援一次觀察多個類"
    foreach (Arr::wrap($classes) as $class) {
        
        $instance->registerObserver($class);
    }
}
複製程式碼

展開 $instance->registerObserver($class);

// "剛才的傳入 $class = 'App\UserObserver'"
protected function registerObserver($class)
{
    $className = is_string($class) ? $class : get_class($class);

    // "這裡就是獲取指定的事件名稱"
    $this->getObservableEvents()  = array_merge(
        [
            'retrieved', 'creating', 'created', 'updating', 'updated',
            'saving', 'saved', 'restoring', 'restored',
            'deleting', 'deleted', 'forceDeleted',
        ],
        $this->observables, // "這個是提供給使用者自己定義的方法"
    );

    foreach ($this->getObservableEvents() as $event) {

        // "還記得 'App\UserObserver' 中定義的方法嗎!"
    
        if (method_exists($class, $event)) {
            static::registerModelEvent($event, $className.'@'.$event);
        }
    }
}
複製程式碼

註冊 Observer 中的事件

上面迴圈了所有的提前指定事件,然後逐個檢查觀察者並進行註冊。

繼續分析 static::registerModelEvent($event, $className.'@'.$event);

protected static function registerModelEvent($event, $callback)
{
    if (isset(static::$dispatcher)) {
        $name = static::class;

        static::$dispatcher->listen("eloquent.{$event}: {$name}", $callback);
    }
}
複製程式碼

statci::$dispatcherlluminate\Events\Dispatcher 物件

所以其實這裡迭代呼叫了,可以看到就是繫結一個個事件,同時將事件 指向UserObserver@functionName的對應方法中。

static::$dispatcher->listen("eloquent.retrieved: App\User", 'App\UserObserver@retrieved');
static::$dispatcher->listen("eloquent.creating: App\User", 'App\UserObserver@creating');
static::$dispatcher->listen("eloquent.created: App\User", 'App\UserObserver@created');
..... 省略類似程式碼
static::$dispatcher->listen("eloquent.forceDeleted: App\User", 'App\UserObserver@forceDeleted');
複製程式碼

關於事件的繫結以及觸發機制請移步【Laravel-海賊王系列】第九章, Events 功能解析

經歷過上面種種繫結我們已經繫結了需要的事件,並且監聽者就是 UserObserver 物件。

接下來就是如何觸發的問題了。

觸發 Observer 監聽的事件

我們來看常用的生成使用者的方法。

User::create(
    [
        'name'     => "big pig's feet",
        'email'    => 'hello@world.com',
        'password' => password_hash('123456', PASSWORD_BCRYPT, ['cost' => 12,]),
    ]
);
複製程式碼

⚠️這裡 create 方法在 Model 中並不存在,會呼叫魔術方法 __call()

我們本章只關心觀察者,查詢構建器後續在涉及!直接看最終呼叫方法

位於 ModelperformInsert 方法

protected function performInsert(Builder $query)
{
    if ($this->fireModelEvent('creating') === false) {
        return false;
    }
    
    if ($this->usesTimestamps()) {
        $this->updateTimestamps();
    }

    $attributes = $this->getAttributes();

    if ($this->getIncrementing()) {
        $this->insertAndSetId($query, $attributes);
    }else {
        if (empty($attributes)) {
            return true;
        }

        $query->insert($attributes);
    }
    
    $this->exists = true;

    $this->wasRecentlyCreated = true;

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

    return true;
}
複製程式碼

這裡我們不詳解如何執行事件的程式碼,我們只關心執行了什麼!

執行邏輯分析:

首先觸發模型之前繫結的 creating 方法。

呼叫最後插入資料的邏輯。

設定一些放重複的屬性

呼叫繫結的 created 事件。

到了這裡可以知道官方給出的許多事件以及執行順序的原因!

結語

LaravelModel 功能非常多,本章只是

簡單的介紹了其中一種功能“觀察者”,還有很多方法沒有分析

但是基本原理我們只要知道一種其他也都是一樣。

最後總結一下

Model 結合 laravel 事件來完成觀察者事件的監聽和觸發。

Provider 提供了觀察者在啟動過程的載入服務。

相關文章