$hidden 是 Laravel 序列化时隐藏敏感字段的第一道防线,仅对 toArray()/toJson() 等序列化生效,对日志、dd()、事件广播、关联模型等完全无效,需按场景分别脱敏。

直接在模型里写 $hidden 数组,是 Laravel 隐藏敏感字段最轻量、最可靠的第一道防线——但它只在模型被序列化(toArray()、toJson()、API Resource 自动调用)时生效,对日志、事件广播、dd()、关联模型字段完全无效。
模型里用 $hidden 控制默认序列化输出
这是最常用也最容易写错的地方。它不是“禁止读取”,而是“不放进数组/JSON”。只要模型实例走到序列化流程,$hidden 就自动起作用。
-
$hidden字段名必须和模型属性名一致,比如数据库字段是password_hash,但模型里定义了getPasswordAttribute(),那要隐藏的是password,不是password_hash - 别和
$guarded混:后者管批量赋值(create()/fill()),跟输出无关 - 常见漏项:
api_token、temp_key、two_factor_secret—— 只要出现在toArray()结果里,就该进$hidden - 关联模型不继承:即使
User隐藏了email,$user->posts里的每个Post模型仍会完整输出,除非Post自己也定义了$hidden
为什么 makeHidden() 对关联字段没用
调用 $user->load('posts')->makeHidden(['email']) 后,posts 数组里每个 Post 的 email 依然存在——因为 makeHidden() 只修改当前模型实例的原始属性,不递归处理集合或嵌套关系。
- 想临时隐藏关联字段,得单独操作:
$user->posts->makeHidden(['content']) - 更稳妥的做法:在关联模型(如
Post)里直接定义$hidden = ['content'] - 如果只是某次响应需要,用
toArray()后手动unset更可控,尤其涉及多层嵌套时 - 注意优先级:
makeHidden(['token'])是追加隐藏,不会取消$hidden里已有的password;想只显email,得先makeVisible(['*'])再makeHidden(['password'])
API 响应中 $hidden 失效的典型场景
你以为加了 $hidden 就万事大吉,结果接口返回里敏感字段还在——大概率掉进了这几个坑:
- 在 Resource 的
toArray()里写了return $this->resource->toArray():这绕过了你写的逻辑,也绕过了$hidden的二次过滤 - Resource 构造时传入的是未处理的模型,但响应体里又直接用了
'user' => $this->user(没调->toArray()):Laravel 会尝试调用jsonSerialize(),但某些中间件或调试工具可能跳过模型可见性检查 - 用了
$visible白名单模式:一旦启用,$hidden和makeHidden()全部失效,Laravel 只认白名单里的字段 - 事件广播里调
$model->toArray():$hidden完全不触发,必须改用$model->only(['id', 'name'])或自定义toBroadcastArray()
日志和事件里 $hidden 为什么完全不生效
因为 $hidden 是序列化层的开关,而日志打印(Log::info($user))、dd()、异常追踪、事件广播数据组装,都不走 toArray() 流程——它们直接访问模型属性或调用 __debugInfo()。
- 日志脱敏不能靠
$hidden,得在 logging channel 的tap处理器里递归过滤$context中的敏感键(如password、card_number) - 事件广播必须显式控制:用
$model->only(),或在broadcastWith()里手工构造数组,绝不能直接传$model->toArray() -
dd($user)或Log::debug($request->all())这类操作,$hidden一概无视;调试时要用$request->except(['password']) - 哪怕你把
password加进$hidden,数据库查询结果、DB::table()->get()返回的原生对象、甚至模型的$model->password属性本身,都照常可读
真正容易被忽略的点是:同一份敏感字段,在不同上下文(API 响应、事件广播、日志、调试输出)要走完全不同的脱敏路径。$hidden 只管其中一条路,其他都得单独补上。


















