Laravel5.8 模型事件與 Observer

Observer 觀察者模式創(chuàng)建步驟

  • 觀察者
    • 監(jiān)聽多個事件


      觀察者
app\Observers\TopicObserver.php

class TopicObserver
{
    //  監(jiān)聽事件  
}
  • 注冊觀察者


    注冊觀察者
app\Providers\AppServiceProviders.php


namespace App\Providers;

use App\Topic;
use App\Observers\TopicObserver;
use Illuminate\Support\ServiceProvider;

class AppServiceProvider extends ServiceProvider
{
    /**
     * 注冊服務(wù)提供.
     *
     * @return void
     */
    public function register()
    {
        //
    }

    /**
     * 運行所有應(yīng)用.
     *
     * @return void
     */
    public function boot()
    {

         // 為 Topic 模型注冊觀察者
        Topic::observe(TopicObserver::class);
    }
}

觀察者中事件的發(fā)生順序

laravel 5.8 版本下對于 Illuminate\Database\Eloquent\Model 源文件的分析,為了方便已去掉注釋


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

        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;
    }

首先會觸發(fā) saving 事件

$this->fireModelEvent('saving')
  • 接著判斷 model 是否新創(chuàng)建
    • 若是新創(chuàng)建,則執(zhí)行 Insert()
    • 若非新創(chuàng)建,則調(diào)用 isDirty () 判斷此 model 是否進行了修改。

如果進行了修改,則會調(diào)用 $this->performUpdate($query) 進行更新操作,如果未進行修改,則直接返回 True

進入 $this->performUpdate($query)

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->syncChanges();

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

        return true;
    }

可以看到此處觸發(fā) updating 方法:

$this->fireModelEvent('updating')

如果返回的不是 false , 那么繼續(xù)往下走,判斷該模型是否使用了 Timestamp,如果使用了,即先更新自身Timestamp:

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

緊接著調(diào)用 getDirty,顧名思義,獲取自身臟值,進入該方法查看

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

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

      return $dirty;
}

果然該方法內(nèi)初始化空數(shù)組$dirty用于存儲自身被修改后的臟值。

你試過打印 Model模型嗎?

舉個栗子

Order {#359
  #table: "order"
  #guarded: array:1 [
    0 => "id"
  ]
  #connection: "mysql"
  #primaryKey: "id"
  #keyType: "int"
  +incrementing: true
  #with: []
  #withCount: []
  #perPage: 15
  +exists: true
  +wasRecentlyCreated: false
  #attributes: array:45 [
    "id" => 2026
    "order_sn" => "2019080617314062621248"
    "customer_id" => 62
    "order_status" => 0
    "shipping_status" => 99
    "pay_status" => 1
  ......
  ]
  #original: array:45 [
    "id" => 2026
    "order_sn" => "2019080617314062621248"
    "customer_id" => 62
    "order_status" => 0
    "shipping_status" => 99
    "pay_status" => 1
  ......
  ]
  #changes: []
  #casts: []
  #dates: array:1 [
    0 => "deleted_at"
  ]
  #dateFormat: null
  #appends: []
  #dispatchesEvents: []
  #observables: []
  #relations: []
  #touches: []
  +timestamps: true
  #hidden: []
  #visible: []
  #fillable: []
  #forceDeleting: false
}

這是我打印的一個 order實例,可以看到其中有 originalattributes 兩個數(shù)組

  • original 保存的是最初始的實例屬性,無法被外部直接修改
  • attributes 數(shù)組可以被自有修改,此方法即是由判斷兩個數(shù)組的差異而返回的臟值

ok,繼續(xù)往下看 performUpdate 的代碼,可以看到會進行判斷是否有臟值:

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

    $this->syncChanges();

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

存在臟值的情況下進入 if,執(zhí)行 update 操作,
update 結(jié)束后會執(zhí)行 syncChanges 同步自身數(shù)據(jù),繼而觸發(fā) updated 事件。

performUpdate 簡單解析完畢

回看 save() 方法的接下來操作

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

if 判斷在 update 成功下,才會觸發(fā) finishSave() 方法,此方法會接受一個參數(shù) $options,即是修改的屬性.
進入該 finishSave()

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

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

    $this->syncOriginal();
}

這里觸發(fā)了 saved 事件

  • 當(dāng)模型已存在,非新建時,事件觸發(fā)順序如下:
    saving -> updating -> updated -> saved
  • 當(dāng)模型不存在,即需要新增時,事件觸發(fā)順序如下:
    saving -> creating -> created -> saved

觸發(fā)順序知曉,那么又有個疑問

saving,savedupdating,updated 有什么不一樣(區(qū)別)?
  • 舉的實例中,LaravelEloquent 會維護實例的兩個數(shù)組,分別是 originalattributes。

    • 兩者都是在 saved 事件觸發(fā)之后,Laravel 才會對兩個數(shù)組執(zhí)行 syncOriginal操作
  • saveingsaved 會在 Eloquent 實例的 original 數(shù)組真值更改前后觸發(fā)

  • updatingupdated 會在數(shù)據(jù)庫中的真值修改的前后觸發(fā)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容