You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Laravel:如何缓存模型关联关系translations获取的数据?

如何缓存Laravel模型关联的翻译数据?

问题背景

现有Country模型及关联的翻译表代码如下:

<?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());
    }
}

尝试两种缓存翻译关联的方案均报错:

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

方案一:查询时给关联加缓存

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

方案二:在关联方法中直接加缓存

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

解决方案

原因分析

remember()是Rememberable trait给**查询构建器(Builder)**添加的方法,而关联返回的HasMany是关系类,并非直接的查询构建器,所以直接调用会报错。

正确实现方式

方法一:缓存包含关联的完整模型查询

直接在主模型查询上使用remember(),同时预加载关联,这样整个查询结果(包括关联的翻译数据)都会被缓存:

$c = Country::with('translations')
    ->remember(600)
    ->find($this->country_id);

这种方式操作简单,适合单次查询需要完整模型+关联数据的场景。

方法二:手动用缓存门面单独缓存关联数据

如果需要单独控制关联数据的缓存规则,可以通过Cache门面手动实现:

// 获取基础Country模型
$country = Country::find($this->country_id);

// 生成带语言标识的唯一缓存键,避免多语言数据冲突
$cacheKey = "country_{$country->id}_translations_" . App::getLocale();

// 缓存翻译数据
$translations = Cache::remember($cacheKey, 600, function () use ($country) {
    return $country->translations()->get();
});

// 将缓存的关联数据绑定到模型上
$country->setRelation('translations', $translations);

这种方式灵活性更高,能针对关联数据单独设置缓存时长或更新规则。

方法三:让翻译模型启用Rememberable特性

如果你的翻译模型(比如CountryTranslation)也引入了Rememberable trait,可以修改关联方法,先获取查询构建器再调用remember():

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

注意:必须确保翻译模型本身已经使用Rememberable trait,否则该方法无效。

额外优化建议

  • 启用缓存标签:取消Country模型中protected $rememberCacheTag = 'countries';的注释,后续更新国家数据时,可通过Cache::tags('countries')->flush();批量清除相关缓存。
  • 缓存键加入语言标识:由于你的查询带有语言全局作用域,缓存时把当前语言加入键名,能避免不同语言的数据互相覆盖。

内容的提问来源于stack exchange,提问作者M a m a D

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.24 10:13:23