接上一篇博客:Laravel Model 優化 - 添加屬性緩存(attribute cache)php
以前實現的AttributeCacheHelper,雖然實現解決每次請求中動態屬性重複觸發SQL執行的問題,可是也引入了一個新的問題,增長了代碼量,維護成本也增長了,添加100個動態屬性,就要實現200個函數,使用數量大起來,算是個噩夢了。segmentfault
下面嘗試改進這個問題,現將全部的緩存屬性整合到一個數組中,經過cacheKey去指向解析函數。數組
首先實現一個數組 cacheable 用於管理attribute與實體函數的映射。緩存
如balance的屬性會映射到refreshBalance函數上。函數
private $cacheable = [ 'posts_count' => 'postCount', 'balancde' => 'refreshBalance', ];
如何處理這個屬性映射呢,可使用 PHP 提供的魔術方法 __ get 來進行處理,而後修改attribute cache helper的 __get 函數,首先會去檢查這個屬性是否在緩存映射數組中,若是存在的話,直接去緩存中獲取該數據,若是數據不存在,則執行映射函數來獲取執行後的結果,並緩存起來,反之,若是屬性不存在緩存映射數組中,則轉發到model自己的 __get 魔術方法中,再進行處理。post
<?php namespace App\Traits; trait AttributeCacheHelper { private $cachedAttributes = []; public function __get($key) { if (array_key_exists($key, $this->cacheable)) { return $this->getCachedAttribute($key, [$this, $this->cacheable[$key]]); } return parent::__get($key); } public function getCachedAttribute(string $key, callable $callable) { if (!array_key_exists($key, $this->cachedAttributes)) { $this->setCachedAttribute($key, call_user_func($callable)); } return $this->cachedAttributes[$key]; } public function setCachedAttribute(string $key, $value) { return $this->cachedAttributes[$key] = $value; } public function refresh() { $this->cachedAttributes = []; return parent::refresh(); } }
完成以上操做後,改進的後屬性緩存,便無需定義兩個函數了,只須要定義一個映射函數,而後將其指向到須要構造的動態屬性中便可。測試
那麼問題又來了,若是實現主動刷新緩存數據呢?尤爲是在支付、提現等場景中,每每最後一步都須要主動刷新緩存,再覈實一遍,那麼再添加一個 refreshAttributeCache 函數,咱們能夠主動的對屬性緩存進行數據刷新。優化
<?php namespace App\Traits; trait AttributeCacheHelper { private $cachedAttributes = []; public function __get($key) { if (array_key_exists($key, $this->cacheable)) { return $this->getCachedAttribute($key, [$this, $this->cacheable[$key]]); } return parent::__get($key); } public function getCachedAttribute(string $key, callable $callable, $refresh = false) { if (!array_key_exists($key, $this->cachedAttributes) || $refresh) { $this->setCachedAttribute($key, call_user_func($callable)); } return $this->cachedAttributes[$key]; } public function setCachedAttribute(string $key, $value) { return $this->cachedAttributes[$key] = $value; } public function refresh() { $this->cachedAttributes = []; return parent::refresh(); } public function refreshAttributeCache($key) { if (array_key_exists($key, $this->cacheable)) { return $this->getCachedAttribute($key, [$this, $this->cacheable[$key]], true); } } }
將 AttributeCacheHelper 附加到模型中。this
<?php namespace App; use App\Model; class Wallet extends Model { use WalletRepo, WalletResolver, AttributeCacheHelper; private $cacheable = [ 'balance' =>'refreshBalance' ]; public function refreshBalance() { $lastTransaction = $this->transactions()->latest('id')->select('balance')->first(); $balance = !is_null($lastTransaction) ? $lastTransaction->balance : 0; return $balance; } }
執行結果。spa
$wallet = Wallet::find(1); for ($i = 0; $i < 10000; $i++) { // 只執行一次真實有效查詢 dump($wallet->balance); } // 從新獲取查詢結果,執行SQL $balance = $wallet->refreshAttributeCache('balance'); dd($balance);
以上就解決掉了當初初版的attribute cache helper 形成的定義函數比較多,比較難維護,沒法主動刷新緩存的弱點,固然也能夠反向操做,將無需緩存的屬性標註出來,其他的屬性都進行緩存起來,我的比較提倡按需所取,hhhh,具體怎麼作仍是要看具體業務使用場景,來作不一樣的處理。