Laravel中Eloquent事件中的Updated和Saved原來不一樣
受任于败军之际,奉命于危难之间,尔来二十有一年矣。
當一個新模型初次被儲存,將會觸發 creating 以及 created 事件。如果一個模型已經存在於資料庫而且呼叫了 save 方法,將會觸發 updating 和 updated 事件。然而,在這兩個狀況下,都將會觸發 saving 和 saved 事件。
Laravel 在資料更新的時候會觸發兩個事件:updated 和 saved。單從官方文件的說明,saved 看起來像是一個方便開發者統一管理新增/更新動作的事件,讓重複的程式碼可以不用被寫在兩個地方(created 和 updated),但是其實 saved 和 updated 的觸發時機是不一樣的。
接來看一下 Laravel eloquent 的原始碼。
1 | // 495 行 |
應該盡量使用 updated 而不是 saved 來判斷資料是否更新
可以看到在觸發 updated 之前會先做一次 getDirty() 的檢查,其實就是在檢查這次更新的資料是不是真的有改動資料的值;相反的,saved 不管值有沒有改動都會觸發。如果沒注意到這個差別的話,這在實務上其實會造成一些問題。
例如,我們通常會把資料的結果快取起來,以減少對資料庫的查詢次數。因此當資料有變動的時候,就會需要把對應的快取刪除。為了避免有漏刪的部分,會把刪除快取的動作綁定在 eloquent 事件中。認清 updated 和 saved 之後,就會知道這個動作應該是要綁在 updated;若是綁在 saved 上,就會造成多餘的快取動作,增加機器的成本。當快取數量一多,又有用到類似 redis scan 這種較耗時的功能來找出需要刪除的快取時,對於機器的效能影響更大。
使用 updated 也仍會可能有漏網之魚
但是,要注意的是,即使使用了 updated 也不能說完全避免這件事的發生。這時候又要再看一次 Larvel eloquent 的原始碼了。
1 | // 3093 行 |
可以發現 Laravel 判斷值有沒有改動的比較依據來自於,我們一開始 select 出來的欄位,跟這次要變動的欄位。意思就是,如果我們只 select 了 A 欄位出來,但是更新的是 B 欄位時,即使更動後 B 欄位的值跟更動之前一模一樣,Laravel 仍會把此視為資料的值有所改動。
不過這仍在可以接受的範圍之內。畢竟每個更新之前,若要為了拿到所有欄位而又再多查詢一次資料庫,可能會是一件更浪費資源的事情。