直接在模型层控制序列化行为,优先使用$hidden(黑名单)或$visible(白名单)控制字段显隐;通过访问器动态计算字段值并参与序列化;API场景推荐使用API Resource统一编排响应结构,实现模型与传输格式解耦。

直接改模型的序列化行为,不是靠 json_encode() 临时处理,而是让 toArray() 和 toJson() 从源头就按你需要的结构和字段输出。关键在模型层控制,而不是控制器里硬塞数组。
用 $hidden 和 $visible 控制字段显隐
这是最轻量、最常用的方式,适用于「始终不暴露某字段」或「只允许输出固定字段」的场景。它作用于所有序列化路径(toArray()、toJson()、API Resource 中的 resolve() 等)。
-
$hidden是黑名单:声明后,字段在任何序列化中都自动剔除,比如protected $hidden = ['password', 'api_token'] -
$visible是白名单:一旦设置,**其他所有字段默认被屏蔽**,哪怕没在$fillable里也一样——这点容易被忽略 - 两者不能共存;若同时定义,
$visible优先级更高,$hidden会被忽略 - 注意:
$hidden对关系(with()加载的关联模型)无效,需单独在关联模型里设
用访问器(Accessors)改字段值或结构
当字段内容需要动态计算、格式转换、多语言适配时,访问器比手动在控制器里 map() 更干净、可复用。
- 例如数据库存的是 JSON 字符串
{"en": "Morocco", "ar": "المغرب"},想通过$country->name直接取当前语言值:public function getNameAttribute() { return json_decode($this->attributes['name'], true)[app()->getLocale()] ?? $this->attributes['name']; } - 访问器名必须是
getXXXAttribute格式,且XXX首字母大写(对应属性名xxx) - 它只影响读取,不影响数据库写入;若还需写入时处理,得配对加修改器(Mutator)
setNameAttribute() - 访问器返回值会参与序列化——所以你可以返回数组、对象,甚至
Carbon实例,它们会被自动转成 JSON 兼容格式
用 API Resource 统一响应结构(推荐用于 API)
如果你的项目有明确前后端分离或对外提供 API,别在模型里堆逻辑。Resource 是 Laravel 官方推荐的「序列化编排层」,能解耦数据结构与业务模型。
- 单个资源用
new UserResource($user),集合用UserResource::collection($users),保证结构一致 - 可在
toArray()方法里自由组合字段、嵌套关系、添加元信息:return [ 'id' => $this->id, 'name' => $this->name, 'avatar_url' => $this->when($this->relationLoaded('avatar'), fn() => $this->avatar->url) ]; -
$this->when()和$this->mergeWhen()支持运行时条件判断,比静态$hidden更灵活 - 不同 API 版本可用不同 Resource 类(如
V1\UserResource/V2\UserResource),避免模型污染
Laravel 9+ 后批量赋值和日期 cast 的连带影响
升级后模型序列化异常,常不是序列化本身的问题,而是前置环节出错导致模型根本没正确加载字段。
- 没定义
$fillable或$guarded→ 模型实例字段为空 →toArray()输出空数组或报MassAssignmentException(Laravel 9+ 默认严格模式) -
casts里写'published_at' => 'date'→ 升级后返回string而非Carbon→ 访问$post->published_at->format()报错「Call to a member function format() on string」 - 解决方法:改用
'published_at' => 'datetime'(返回Carbon)或显式封装Carbon::parse($this->published_at) - 这些看似和「序列化」无关,但实际决定了
toArray()里某个键的值类型——而 JSON 只认基本类型,对象若没正确转成数组/字符串,就会被丢弃或报错
真正难调的不是怎么写 Resource 或访问器,而是搞清哪一层该承担什么责任:模型管数据形态,Resource 管传输结构,控制器只负责调度。混在一起改,很快就会出现字段忽隐忽现、本地正常线上报错、API 版本混乱等问题。











