问题背景

接上一篇博客:Laravel Model 优化 - 增加属性缓存(attribute cache)

之前实现的AttributeCacheHelper,尽管实现解决每次申请中动静属性反复触发SQL执行的问题,然而也引入了一个新的问题,减少了代码量,保护老本也减少了,增加100个动静属性,就要实现200个函数,应用数量大起来,算是个噩梦了。

实现缓存映射

上面尝试改良这个问题,现将所有的缓存属性整合到一个数组中,通过cacheKey去指向解析函数。

首先实现一个数组 cacheable 用于治理attribute与实体函数的映射。

如balance的属性会映射到refreshBalance函数上。

private $cacheable = [        'posts_count' => 'postCount',        'balancde'    => 'refreshBalance',];

如何解决这个属性映射呢,能够应用 PHP 提供的魔术办法 __ get 来进行解决,而后批改attribute cache helper的 __get 函数,首先会去查看这个属性是否在缓存映射数组中,如果存在的话,间接去缓存中获取该数据,如果数据不存在,则执行映射函数来获取执行后的后果,并缓存起来,反之,如果属性不存在缓存映射数组中,则转发到model自身的 __get 魔术办法中,再进行解决。

<?phpnamespace 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 函数,咱们能够被动的对属性缓存进行数据刷新。

<?phpnamespace 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 附加到模型中。

<?phpnamespace 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;    }}

执行后果。

$wallet = Wallet::find(1);for ($i = 0; $i < 10000; $i++) {    // 只执行一次真实有效查问    dump($wallet->balance);}// 从新获取查问后果,执行SQL$balance = $wallet->refreshAttributeCache('balance');dd($balance);

结尾

以上就解决掉了当初第一版的attribute cache helper 造成的定义函数比拟多,比拟难保护,无奈被动刷新缓存的弱点,当然也能够反向操作,将无需缓存的属性标注进去,其余的属性都进行缓存起来,集体比拟提倡按需所取,hhhh,具体怎么做还是要看具体业务应用场景,来做不同的解决。