Eloquent ORM Cache

Laravel Eloquent ORM Cache

Whole Eloquent Model Cache Strategy

1. Cache array in the database

You can only store the eloquent array of data in the cache to reduce the stored memory usage

Can NOT use toArray() method because the toArray() method will change the casting data (e.g. AsCollection, AsArrayObject) to the other data format. We need to keep the original data from the database, so we have to use the getAttributes() to get the original data

public function putEloquentModelCache($Model, $cache_key, $minutes = null)
{
    if ($Model instanceof Model) {
        $id = $Model->getKey();
        $cache_key = sprintf('Model:%s', $id);
        // XXX $cache_data = $Model->toArray();
        $cache_data = $Model->getAttributes();

        Cache::put($cache_key, $cache_data, $minutes);
    }
}

2. Get the eloquent model array cache

After you get the eloquent array of data from the cache, we still want to manipulate these data as the Eloquent ORM. So we have to transform back to the original eloquent model.

You can use the Eloquent newFromBuilder() method to fill all these data back, to tell Eloquent these data are the original data from the database.

public function getEloquentModelCache(string $id, string $model_class_name)
{
    $Model = null;

    if (class_exists($model_class_name)) {
        $TmpModel = new $model_class_name;
        if ($TmpModel instanceof Model) {
            $id = $TmpModel->getKeyName();
            $cache_key = sprintf('Model:%s', $id);
            $cache_data = Cache::get($cache_key);

            if (!is_null($cache_data)) {
                // The model exist then fill the model data from cache and sync back to the original attribute
                // XXXX $Model = $TmpModel->fill($cache_data)->syncOriginal();
                $Model = $TmpModel->newFromBuilder($cache_data);
            }
        }
    }

    return $Model;
}

If you DO NOT call the Eloquent newFromBuilder() method, then the eloquent will think all of these data are new data. So when you execute the Eloquent update() or save() methods, it will insert these data into the database.

So if you want to treat these data like they have already in the database when you execute the Eloquent update() or save() methods. If you want them to update data instead of insert data, then you should call the Eloquent newFromBuilder() method.

Other Eloquent Unique Key Cache Strategy

We always have the primary key on the database table to let us find it. But sometimes we may have another unique table field to let us query the data in different ways.

For instance, we have the primary key id on the user table. But the email and username fields might be unique too. So we can find the right data from these 3 fields.

id name email username
u-xxxx KJ kj@example.com kj
u-kkkk Kay kay@example.com kay
u-jjjj Jay jay@example.com jay

But if we want to cache the SAME user data in the 3 different keys. The cached user data will be duplicated and hard to maintain.

So the main goal is to store 1 user data on the cache, and the other cache data will reference back to the same data. If the cached user data is updated then everything will update.

1. Only cache the primary key for other unique keys

We only cache the primary key value to the unique key cache, so it will only store the single primary key id string on the cache database

/**
 * @param $Model
 * @param $unique_key_field_name
 * @param $minutes
 * @return void
 * @throws Exception
 */
public function putModelIdMappingCache($Model, $unique_key_field_name, $minutes = null)
{
    if ($Model instanceof Model) {
        // Model primary key value
        $id = $Model->getKey();
        // Model unique key value
        $unique_key_field_value = $Model->{$unique_key_field_name};

        $cache_key = sprintf('ModelUniqueKey:[%s:%s]', $unique_key_field_name, $unique_key_field_value);
        // Store the primary key to the unique key cache
        $cache_data = $id;
        Cache::put($cache_key, $cache_data, $minutes);
    }
}

2. Get the unique key cache and transform back

If we get the unique key data from the cache, we will ONLY get the primary key value for this eloquent model

Then we can use the original getEloquentModelCache() method on the above to get the original eloquent model

/**
 * @param $unique_key_field_name
 * @param $unique_key_field_value
 * @param $model_class_name
 * @return Model|null
 * @throws Exception
 */
public function getModelIdMappingCache($unique_key_field_name, $unique_key_field_value, $model_class_name)
{
    $Model = null;
    $cache_key = sprintf('ModelUniqueKey:[%s:%s]', $unique_key_field_name, $unique_key_field_value);

    $id = Cache::get($cache_key);

    if (!is_null($id)) {
        // Call original model id cache method
        $Model = $this->getEloquentModelCache($id, $model_class_name);
    }

    return $Model;
}