乐趣区

关于laravel:Laravel-Model-优化-属性缓存attribute-cache-优化

问题背景

接上一篇博客: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 魔术办法中, 再进行解决。

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

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

执行后果。

$wallet = Wallet::find(1);
for ($i = 0; $i < 10000; $i++) {
    // 只执行一次真实有效查问
    dump($wallet->balance);
}

// 从新获取查问后果, 执行 SQL
$balance = $wallet->refreshAttributeCache('balance');
dd($balance);

结尾

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

退出移动版