前段时间作项目时候,想要在不改变方法签名的状况下,给 Model::find 方法作个缓存。并且想要作到即插即用。node
1.先看一下当咱们调用 find 方法时,框架干了什么?json
找到 Illuminate\Database\Eloquent\Model 的代码,搜索 find,没有该方法。看来是走了 __callStatic 这个魔术方法。该方法里只有一行代码:缓存
return (new static)->$method(...$parameters);
static 指的是调用该静态方法的类(若是使用的是 UserModel::find(1),则 static 就表明 UserModel 类)。看来是实例化了一个对象,并调用了成员方法。架构
2.分析如何优雅地在中间插一脚框架
为了可以在调用 find 时候,先走咱们的缓存,因此咱们须要覆盖 __callStatic 方法,并检测若是是 find 方法,则优先返回缓存中的数据。this
另外,为了可以达到即插即用的效果,咱们使用继承的方式,而是使用了 Trait。核心逻辑以下:spa
public static function create($data = null){ if ($data == null){ return null; } $instance = new static; foreach ($data as $key => $value){ $instance[$key] = $value; } return $instance; } /** * 若是方法是 find($id, $nocache) * * @param string $method * @param array $parameters * @return mixed */ public static function __callStatic($method, $parameters) { if ($method == 'find'){ // 从缓存中获取数据 $obj = static::create(json_decode(Redis::get(static::getCacheKey($parameters[0])), true)); if (null == $obj){ $obj = (new static)->$method(...$parameters); if (null == $obj){ return null; } else { $key = static::getCacheKey($parameters[0]); // 设置缓存及过时时间 Redis::set($key, $obj); Redis::expire($key, static::$expire_time); return $obj; } } else { $obj->exists = true; return $obj; } } else if($method == 'findNoCache'){ $method = 'find'; return (new static)->$method(...$parameters); } return (new static)->$method(...$parameters); } private static function getCacheKey($id){ $name = str_replace('\\', ':', __CLASS__); return "{$name}:{$id}"; }
大致逻辑上面已经介绍过了:覆盖 __callStatic 方法,判断若是是调用 find ,则走缓存(无缓存,查询后须要设置缓存)。另新增 findNoCache 方法。code
3.细节补充对象
当修改(或删除)数据(调用 save 方法)时须要删除已缓存的内容。blog
private static function clearCache($id){ Redis::del(self::getCacheKey($id)); } /** * when save, should clear cache * @param array $options */ public function save(array $options = []){ static::clearCache($this[$this->primaryKey]); return parent::save($options); } // delete 方法我暂时写,内容相似 save 方法 如何使用。在须要使用 find 缓存的 Model 类里,加上一行就够了。 class User extends BaseModel { use MemoryCacheTrait; }
快去试试吧。
更多PHP内容请访问: