$hidden 和 $visible 不应用于 api 字段过滤,因其影响全局序列化且无法按场景差异化;应使用 resource 类的 only()、makevisible() 及策略动态控制字段。

Eloquent 的 $hidden 和 $visible 不是“细粒度字段控制”工具,强行用它们做 API 字段过滤,大概率会踩坑、难维护、和前端约定脱节。
为什么 $hidden / $visible 不能当 API 字段开关用
这两个属性作用于模型实例的全局序列化行为(比如 toArray()、toJson()),一旦设了,所有调用处都生效——控制器、队列任务、日志记录、缓存序列化全被影响。
-
$hidden = ['password', 'remember_token']看似安全,但若某接口需要返回password_hash(如管理员调试),就得临时unset($model->hidden)或 clone 模型,逻辑污染严重 -
$visible = ['id', 'name', 'email']更危险:漏写字段就永远不出现,新增字段默认不可见,团队协作时极易遗漏,且无法按角色/场景差异化 - 和 API 版本、客户端类型(Web/iOS/Android)、权限级别完全解耦不了
真正可控的字段过滤:用 only() / makeVisible() 在响应前显式裁剪
把字段控制权收回到控制器或资源类里,谁负责输出,谁决定字段。这是 Laravel 官方推荐的 Resource 层职责。
- 在
Resource类中用$this->resource->only(['id', 'name', 'avatar_url'])明确声明要哪些字段,不依赖模型配置 - 需要临时暴露隐藏字段?直接调用
$this->resource->makeVisible('api_token'),只对当前响应生效 - 配合策略(Policy)动态判断:
auth()->user()->can('view_email', $user) ? ['email'] : [],再合并进only() - 避免在模型里写
protected $casts = ['email' => 'encrypted']这类副作用逻辑——加密/脱敏应由资源或服务层处理
复杂场景:多版本 API 或不同客户端需不同字段集
硬编码 only() 列表会重复、难扩展。建议用配置驱动或方法映射。
- 在 Resource 类里定义方法:
public function toArrayForApp(): array { return $this->resource->only(['id', 'name', 'bio']); } - 通过请求头识别客户端:
request()->header('X-Client') === 'ios' ? $resource->toArrayForIOS() : $resource->toArray() - 或用配置文件
config/api_fields.php维护各 endpoint 字段白名单,运行时读取:$fields = config("api_fields.{$routeName}", ['id', 'name']) - 注意:不要把字段列表存在数据库或缓存里动态加载——增加 IO、降低可读性、调试困难
最容易被忽略的一点:Eloquent 访问器(Accessors)也受 $hidden 影响
如果定义了 getFullNameAttribute(),又把它加进了 $hidden,那这个访问器返回的值永远出不来,哪怕你手动调用 $user->full_name 也会被序列化过程过滤掉。
- 验证方式:打印
dd($user->toArray())和dd(['full_name' => $user->full_name]),看是否一致 - 正确做法:访问器本身不该进
$hidden;字段过滤统一交给 Resource 或only()处理 - 若访问器依赖敏感字段(如拼接 email),应在访问器内部做权限检查,而不是靠
$hidden掩耳盗铃
字段可见性不是模型该管的事,是响应契约的一部分。把控制权留在离输出最近的地方,才能应对权限变化、API 迭代和跨端差异——模型只管数据结构,Resource 只管怎么呈现。
大量免费API接口:立即使用
涵盖生活服务API、金融科技API、企业工商API、等相关的API接口服务。免费API接口可安全、合规地连接上下游,为数据API应用能力赋能!











