在 Laravel
的世界中,你對 Eloquent
大多數操作都會或多或少的觸發一些模型事件,今天就來看一下模型事件的使用。
Laravel
事先已經定義好了 10
個模型事件以供我們使用,它們分別是:
creating
, created
, updating
, updated
, saving
, saved
, deleting
, deleted
, restoring
, restored
。
事件名稱都很淺顯易懂,如果你是認真寫程式碼話都應該可以看明白,如果不明白的話可以左轉百度。
不過你可能對 updating
, updated
, saving
, saved
這四個事件有所疑惑,所以我們來詳細分析一下。
開啟 Illuminate\Database\Eloquent\Model
,找到位於 517
行的 save
方法
筆者使用的是
Laravel 5.5
最新版本,如果你使用的是其他版本,請自行在官方文件版本差異
中對比你的版本
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;
}
可以看到首先觸發的是 saving
:
$this->fireModelEvent('saving')
接著會判斷此 model
是不是新建立的。
如果是新建立的,那麼則會進行 insert
操作,由於本例是分析修改操作,所以這裡直接略過。
如果不是新建立的,那麼會呼叫 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;
}
意料之中,此方法內會返回自身被修改後的髒值,如果你列印過 Laravel
的 Model
例項,那麼相信你會看到例項中有 original
和 attributes
兩個陣列。
其中 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
,saved
和 updating
,updated
到底有什麼區別呢?
上面已經講過,Laravel
的 Eloquent
會維護例項的兩個陣列,分別是 original
和 attributes
。
只有在 saved
事件觸發之後,Laravel
才會對兩個陣列執行 syncOriginal
操作,這樣就很好理解了。
updating
和 updated
會在資料庫中的真值修改前後觸發。
saving
和 saved
則會在 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
中觀測到各種事件啦~
本作品採用《CC 協議》,轉載必須註明作者和本文連結