Eloquent 的 $casts、访问器和修改器不支持多语言属性状态自动切换;重写 getAttribute 会导致类型转换丢失等问题;推荐使用 spatie/laravel-translatable,它通过 JSON 字段和标准访问器机制安全实现多语言。

Eloquent 的 $casts、get* 访问器、set* 修改器本身不支持“多语言属性状态”的自动切换——所谓 “Attribute Globalization States”,Laravel 官方并没有这个概念,它是社区对多语言字段处理的模糊表述,容易引发配置错位和运行时异常。
为什么直接在 Eloquent 模型里写 getAttribute 会失效
很多人试图重写 getAttribute 方法来动态返回当前 locale 对应的字段值(比如 name_zh / name_en),但这条路走不通:Eloquent 在底层用 __get 触发属性访问时,已先经过了 getAttribute,而该方法默认只查原始字段名(如 name),不会自动映射到带 locale 后缀的列;更关键的是,它绕过了 $casts 和访问器逻辑,导致类型转换丢失、JSON 字段解析失败、Carbon 实例变字符串等问题。
- 不要覆盖
getAttribute或__get—— 它们是 Eloquent 的核心路径,改动后易与toArray()、序列化、批量赋值等行为冲突 - 如果真要动态字段名,必须配合
app()->getLocale()手动拼接,且仅限于读取场景;写入时仍需明确指定字段,否则数据库存错列 - 注意:
getAttribute返回的是原始数据库值,不是最终对外暴露的值,中间可能还有访问器加工
spatie/laravel-translatable 是目前最稳的方案
它把多语言字段存在 JSON 列(如 name 字段存 {"zh":"中文","en":"English"})或单独表中,通过 trait 注入统一访问逻辑,避免字段爆炸和 SQL 冗余。关键是它不侵入 Eloquent 核心,所有行为都基于标准的访问器/修改器机制。
- 安装后,在模型中加
use Translatable;和public $translatable = ['name', 'description']; - 字段必须是 JSON 类型(MySQL 5.7+)或 TEXT(兼容旧版),不能是 VARCHAR 直接存多语言字符串
- 读取时用
$model->name自动返回当前 locale 值;显式指定用$model->getTranslation('name', 'ja') - 写入时调用
$model->setTranslation('name', 'fr', 'Français')+$model->save(),否则不会持久化 - 慎用
fill()批量赋值:它默认忽略 translatable 字段,需手动fill($data)->save()并确保$data包含name数组结构
用 accessor + mutator 手动实现要注意字段命名冲突
如果你坚持用分离字段(title_zh, title_en),那必须用访问器/修改器桥接,但得避开 Laravel 的保留字段名和自动检测逻辑。
- 定义
getTitleAttribute,内部根据app()->getLocale()返回对应字段,但别在$casts里声明title,否则类型转换会作用在拼接后的字符串上,出错 - 写入时必须用
setTitleAttribute把值存进对应 locale 字段,不能依赖fillable直接赋值title—— 那样只会写空或报错 - 搜索时不能写
where('title', 'xxx'),得用where("title_".app()->getLocale(), 'xxx'),且注意索引失效问题 - API 返回前建议显式调用
$model->makeHidden(['title_zh', 'title_en']),防止敏感字段泄露
真正麻烦的从来不是“怎么让属性变多语言”,而是“怎么让关联查询、排序、全文检索、缓存键生成、队列任务里的模型状态”也同步适配 locale 变化——这些地方很容易漏掉 locale 上下文,导致后台取到英文、前端展示中文却查不到数据。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











