About Laravel ORM caching the Model::find method

When working on a project some time ago, you want to cache the Model::find method without changing the method signature. And want to be plug and play.

1. First, let's see what the framework does when we call the find method?

Find the code of Illuminate\Database\Eloquent\Model, search find, there is no such method. It seems to be leaving__ Call static is a magic method. There is only one line of code in this method:

return (new static)->$method(...$parameters);

  

Static refers to the class that calls the static method (if UserModel::find(1) is used, static represents the UserModel class). It appears that an object is instantiated and a member method is called.

2. Analyze how to put a foot in the middle gracefully

In order to be able to walk away from our cache when calling find, we need to override__ Call static method, and detect that if it is find method, the data in cache will be returned first.

In addition, in order to achieve the effect of plug and play, we use the way of inheritance, but use the trade. The core logic is as follows:

public static function create($data = null){
 if ($data == null){
 return null;
 }
 $instance = new static;
 foreach ($data as $key => $value){
 $instance[$key] = $value;
 }
 return $instance;
}
/**
 * If the method is find($id, $nocache)
 *
 * @param  string  $method
 * @param  array  $parameters
 * @return mixed
 */
public static function __callStatic($method, $parameters)
{
 if ($method == 'find'){
 // Get data from cache
 $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]);
 // Set cache and expiration time
 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}";
}

  

The general logic has been introduced above: coverage__ callStatic method, judge if find is called, cache will be left (no cache, cache needs to be set after query). Another new findNoCache method is added.

3. Details supplement

When modifying (or deleting) data (calling the save method), you need to delete the cached content.

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);
}
// I write the delete method temporarily. The content is similar to the save method
//How to use. In the Model class that needs to use the find cache, one line is enough.
class User extends BaseModel
{
 use MemoryCacheTrait;
}

  

Go and have a try.

For more PHP content, visit:

Tencent T3-T4 standard boutique PHP architect tutorial directory, as long as you read it to ensure a higher salary (continuous update)

Tags: PHP Redis Database

Posted on Wed, 27 May 2020 07:12:29 -0700 by stanleybb