如何让Laravel属性转换仅获取单个JSON字段值?
解决Laravel模型JSON字段获取指定语言值的问题
问题分析
你遇到的核心问题:
- 定义与字段同名的访问器时,Laravel会跳过
casts的自动转换规则,访问器拿到的是数据库中原始的JSON字符串而非数组,因此用$value['en']会触发"Cannot access offset of type string on string"错误。 - 直接在访问器中调用
$this->title会触发访问器自身,形成循环引用,最终拿到的是访问器返回的字符串而非原始数组,同样引发错误。
解决方案
方案1:使用独立命名的访问器(推荐)
保留原有的casts配置,创建新的访问器处理本地化内容,避免和字段名冲突:
// 保留原有casts配置 protected function casts(): array { return [ 'title' => 'json', 'resume' => 'json', 'content' => 'json', ]; } // 创建本地化标题的访问器 protected function localizedTitle(): Attribute { return Attribute::make( get: fn () => $this->title[app()->getLocale()] ?? $this->title['en'], ); } // 自动将本地化字段加入模型的JSON输出 protected $appends = ['localized_title'];
这样模型转为JSON时,会自动包含localized_title字段,值为当前语言的标题。如果不需要保留原始title字段,可在资源类中隐藏它。
方案2:在访问器中手动处理JSON转换
若一定要复用title字段名,需在访问器中手动将JSON字符串转为数组:
// 移除casts中的title配置,由访问器处理转换 protected function casts(): array { return [ 'resume' => 'json', 'content' => 'json', ]; } protected function title(): Attribute { return Attribute::make( get: function ($value) { $titleArray = json_decode($value, true); return $titleArray[app()->getLocale()] ?? $titleArray['en']; }, // 如需保存值,根据需求调整,示例为按当前语言保存多语言结构 set: function ($value) { return json_encode([app()->getLocale() => $value]); }, ); }
方案3:在资源类中处理(适合API场景)
如果是给前端返回API数据,推荐直接在资源类中处理本地化逻辑:
namespace App\Http\Resources; use Illuminate\Http\Resources\Json\JsonResource; class ArticleResource extends JsonResource { public function toArray($request) { // 优先从请求头取语言, fallback到系统默认语言 $locale = $request->header('Accept-Language') ?? app()->getLocale(); return [ 'id' => $this->id, 'title' => $this->title[$locale] ?? $this->title['en'], 'resume' => $this->resume[$locale] ?? $this->resume['en'], // 其他需要返回的字段 ]; } }
控制器中使用资源类返回数据:
return ArticleResource::collection(Article::all());
前端就能直接拿到仅含目标语言内容的JSON结构,比如[{"id":1,"title":"English Title"}]。
内容的提问来源于stack exchange,提问作者Diego Souza
相关产品推荐
相关产品推荐

