Laravel ORM caching example for Model:: find method

  • 2021-09-16 06:30:24
  • OfStack

Preface

When I was working on the project some time ago, I wanted to cache the Model:: find method without changing the method signature. And want to do plug and play. The following words are not much to say, let's take a look at the detailed introduction.

1. What does the framework do when we call the find method?

Find the code for Illuminate\ Database\ Eloquent\ Model and search for find. There is no such method. It seems that we have gone the magic method of __callStatic. 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 (static stands for the UserModel class if UserModel:: find (1) is used). It appears that 1 object was instantiated and the member method was called.

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

In order to be able to go to our cache first when calling find, we need to override the __callStatic method and detect that if it is an find method, the data in the cache will be returned first.

In addition, in order to achieve plug and play, we use inheritance instead of Trait. 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 the 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]);
    //  Setting 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: override the __callStatic method, and judge that if find is called, it will go to cache (no cache, cache needs to be set after query). In addition, findNoCache method is added.

3. Add details

Cached content needs to be deleted when modifying (or deleting) data (calling the save method).


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  I write the method temporarily, and the content is similar  save  Method 

How to use it. In the Model class that needs to use find cache, just add one line.


class User extends BaseModel
{
 use MemoryCacheTrait;
}

Go and try it.

Summarize


Related articles: