ThinkPHP模型用$hidden属性可屏蔽敏感字段,仅影响toArray()等序列化方法;动态过滤需用withoutField()或visible();注意toArray(true)会绕过隐藏规则,手动组装数组也会失效。

模型里用 hidden 属性控制字段不返回
ThinkPHP 的模型默认会把所有字段都转成数组或 JSON 返回,敏感字段(比如 password、token、is_deleted)必须显式屏蔽。最直接的方式是在模型类里定义 hidden 数组:
-
hidden是模型的静态属性,值为字符串数组,写字段名即可,不需要加表前缀 - 它只影响
toArray()、toJson()、__toString()和 API 直接输出模型实例的场景 - 对
select()原生查询、field()指定字段、value()单字段获取完全无效——这些走的是底层查询,不经过模型序列化逻辑
class User extends Model
{
protected $hidden = ['password', 'salt', 'remember_token'];
}
需要动态隐藏字段?用 withoutField() 或 visible()
硬编码 hidden 不够灵活,比如管理员能看全部字段,普通用户要过滤。这时不能改模型定义,得运行时控制:
-
withoutField(['password'])仅对当前查询结果生效,返回的是Collection或数组,不是模型实例,后续调不到模型方法 -
visible(['id', 'name', 'email'])更彻底——它会丢弃所有没列出来的字段,连id都可能被砍掉,务必核对完整字段列表 - 二者都不影响数据库查询本身,只是在结果组装阶段做裁剪,性能损耗极小
- 注意:如果用了
with()关联预加载,withoutField()对关联模型无效,得进关联模型里单独设hidden
toJson() 还吐出敏感字段?检查是否用了 toArray(true)
有些同学发现设置了 hidden,但 toJson() 依然返回密码字段,大概率是手动调了 toArray(true) ——这个 true 参数表示“忽略隐藏/显示规则,强制全量导出”:
-
$user->toArray()✅ 尊重hidden -
$user->toArray(true)❌ 绕过所有字段过滤,包括hidden和visible -
$user->toJson()底层调的是toArray(),所以也受hidden约束;但一旦中间有人手写了toArray(true),后面再toJson()也没用 - 搜索项目里所有
toArray(,确认参数是不是true,尤其注意封装的通用响应函数
API 接口里字段还在?可能是 JSON 转换前被重新赋值了
后端拼数组、手动构造响应体时,容易把模型对象又转成数组,再塞进大数组里——这一步会丢失模型的 hidden 行为:
立即学习“PHP免费学习笔记(深入)”;
- 错例:
['data' => $user->toArray()]→ 正常,受控 - 错例:
['data' => ['id' => $user->id, 'name' => $user->name, 'password' => $user->password]]→ 手动取值,password白给 - 更隐蔽的坑:用了
__get()触发器或访问器(getPasswordAttr),但没在hidden里声明字段名,而是声明了访问器名(如'password_attr'),这无效,必须写原始字段名 - 调试建议:在返回前
dump($user->toArray()),确认模型本身是否干净,再查外层组装逻辑
hidden,重点盯住数据流出的每个出口。


















