快取巢狀關聯
最近在公司專案中想要快取 Laravel 的模型,因為載入了很多巢狀關聯的模型,使用 json 系列函式並不能輕鬆地將 json 重新轉回模型物件,所以使用了 serialize 系列函式
$json = $redis->get($cacheKey);
$user = $json ? new User(json_decode($json, true)) : User::find($id);
// 模型沒有關聯時,可以直接從 json 取得資料填充新模型
$ser = $redis->get($cacheKey);
$user = $ser ? unserialize($ser) : User::with('a.b')->find($id);
反序列化不呼叫構造方法
起初並沒有什麼問題,從快取中取出反序列化後就能像一般的模型正常使用,直到我需要在模型的建構函式中初始化一些屬性。因為 unserialize
並不會呼叫構造方法,所以在訪問這些屬性時是並未初始化的空值。在反序列化時觸發的 __wakeup
魔術方法中也要做屬性的初始化操作,完成後問題就解決了
public function __construct(array $attributes = [])
{
parent::__construct($attributes);
$this->srv = new UserService();
}
public function __wakeup()
{
parent::__wakeup();
$this->srv = new UserService();
}
全域性靜態初始化方法 booted
我之後注意到 Laravel 提供了一個靜態的全域性初始化方法可供重寫 booted
,如果需要初始化的屬性可以是靜態時,這樣做或許更優雅吧
protected static function booted()
{
self::$srv = new UserService();
}
如果 IDE 沒有方法提示,可以加註釋
/* @var UserService */
static $srv;
呼叫 refresh 的問題
在拿到快取的模型後,如果需要重新從資料庫讀取資料,呼叫 refresh
只會讀取模型本身的關聯,不會讀取巢狀的關聯。需要重新 load
一遍關聯,或者在對應模型的 with
陣列加入每次需要帶上的關聯
$user->refresh();
$user->load('a.b');
好像有人也發現了這個問題,提了 issue Eloquent - nested relations lost when calling refresh on model,Dries Vints(Laravel 專案維護者)回覆是“這似乎是一個問題而不是 bug,建議到社群討論”。我想了下,覺得他是對的,因為 with
上面附帶的關聯時作用於那次查詢的,跟查詢出來的模型沒有關係。不看查詢只看模型的話,要重新載入關聯,那麼只能自己 load
所需的關聯,或者在模型的 with
屬性帶上關聯
備註
- Laravel 模型快取框架有很多,站裡也有教程。不過公司用的是原生 PHP,只是我為了方便單獨引入 Laravel 的 Eloquent 模型,所以那些快取框架無法使用
- 我對 Laravel 模型用法的理解可能有偏差,如果有錯誤請指出,有更好的方法請賜教
本作品採用《CC 協議》,轉載必須註明作者和本文連結