接上一篇博客: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,具体怎么作仍是要看具体业务使用场景,来作不一样的处理。