Search code examples
laravelcachingeloquenteloquent-relationship

Laravel: How to cache the data retrieved from a relationship of a model?


There is a model Country and relation translations as follows

<?php

namespace Modules\User\Entities;

use Astrotomic\Translatable\Translatable;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Support\Facades\App;
use Illuminate\Support\Facades\Cache;
use Modules\Core\Traits\Rememberable;
use Modules\User\Entities\Sentinel\User;

class Country extends Model
{
    use Rememberable;
    use Translatable;

    protected $table = 'countries';
    public $translatedAttributes = ['name'];
    protected $fillable = [
        'name',
        'code',
        'weight',
    ];

//    protected $rememberCacheTag = 'countries';

    public function users()
    {
        return $this->hasMany(User::class, 'country_id');
    }

    protected static function booted()
    {
        static::addGlobalScope('lang', function (Builder $builder) {
            $builder->whereHas('translations', function (Builder $q) {
                $q->where('locale', App::getLocale());
            });
        });
    }

    public function translations(): HasMany
    {
        return $this->hasMany($this->getTranslationModelName(), $this->getTranslationRelationKey());
    }
}

I need to cache the data of the translations relation. I have tried the following so far that taken from Link

$c = Country::with([
    'translations' => function($q){
        $q->remember(600);
    }
])->remember(600)->find($this->country_id);

and this solution too

public function translations(): HasMany
{
    return $this->hasMany($this->getTranslationModelName(), $this->getTranslationRelationKey())->remember(600);
}

But I got the error

Call to undefined method Illuminate\Database\Eloquent\Relations\HasMany::remember()

How can I cache the data retrieved from a relation?


Solution

  • You should cache the entire query, since just caching the relation does not work. (At least out of the box, unsure if you can 'hack' it)

    So, using Laravel Cache, it becomes as follows;

    $countryId = $this->countryId;
    return Cache::remember("<your_cache_key>", 600, function() use ($countryId) {
       return Country::with(['translations'])->find($countryId);
    });
    

    If you are using Redis/Predis (or anything else that supports cache tagging), you can even add cache tags to it;

    $countryId = $this->countryId;
    return Cache::tags([<array_of_cache_tags>])->remember("<your_cache_key>", 600, function() use ($countryId) {
       return Country::with(['translations'])->find($countryId);
    });
    

    In the example <your_cache_key> could be something along the lines of "country_translations:{$countryId}".

    <array_of_cache_tags> could be something like ["country", "translations"]