$visible 是 ThinkPHP 模型导出字段白名单机制,仅在转数组/JSON/XML时生效,须声明为 protected 且字段名严格匹配数据库列名;动态控制应使用 visible() 实例方法,访问器字段默认不参与过滤,$hidden 黑名单不可动态赋值,最安全方式是在查询阶段用 field() 限定字段。

$visible 是 ThinkPHP 模型中控制输出字段白名单的核心机制,它不改变数据库查询,只在模型转数组、JSON 或 XML 时生效——本质是“导出过滤”,不是“查询过滤”。
必须声明为 protected 才生效
常见失效原因:写成 public $visible = ['id', 'name'] 或漏掉修饰符。只有 protected $visible 被框架识别,private 和 public 均无效。字段名须与数据库列名**完全一致**(区分大小写、下划线),例如数据库字段是 user_name,就不能写成 userName,除非你已关闭下划线转驼峰。
动态调用 visible() 更实用
硬编码在模型里会限制复用性。同一模型在列表页只需 ['id', 'title'],详情页却要 ['id', 'title', 'content', 'category_id'],这时应改用实例方法:
- 调用顺序必须是
$model->visible(['id', 'title'])->toJson();$model->toJson()->visible()无效(toJson()返回字符串) -
visible([])表示“不返回任何字段”,不是“恢复全部”;想返回全部,直接不调用该方法 - 关联模型字段(如
profile.nickname)不受父模型visible()影响,需单独对关联模型调用或在其定义中设置$visible
访问器字段默认不受控
定义了 getFullnameAttr() 这类访问器,即使不在 $visible 列表中,toArray() 仍会包含 fullname 键。这不是 bug,而是设计逻辑:访问器被视为“计算字段”,默认开放。
立即学习“PHP免费学习笔记(深入)”;
- 若需统一管控,建议改用
append(['fullname'])显式追加,这类字段才参与$visible过滤 - 或在访问器内部手动判断:
if (in_array('fullname', $this->getVisible())) { ... }
比 $hidden 更安全可靠
$hidden 是黑名单机制,一漏即泄露;且无法动态修改——控制器里写 $model->hidden = ['password'] 完全无效(它是类属性,非实例属性)。真正能动态生效的只有 makeHidden() 或 setHidden() 实例方法,但要注意:
-
makeHidden()对集合无效,批量操作需用$users->setHidden(['password']) - 若最终输出绕过模型序列化(如
json_encode($model->attributes)),所有$hidden和实例方法都彻底失效 - 最稳的防线仍是查询阶段就砍字段:
UserModel::field(['id', 'name'])->select(),既省带宽又防泄露



















