api资源需手动控制json结构,toarray()决定输出字段、嵌套、条件与格式;须预加载关联防止n+1,用whenloaded()安全嵌套,避免暴露敏感信息。

API 资源不是“写完就能用”的自动转换器,toArray() 返回什么,JSON 就长什么样;它不改模型数据,只决定输出结构。你得亲手控制字段、嵌套、条件和格式,否则容易漏字段、暴露敏感信息、或触发 N+1。
怎么定义单个资源的 JSON 结构
资源类的 toArray() 方法就是最终响应体的源头。返回数组的键名直接变成 JSON 字段名,值可以是原始属性、访问器、其他资源实例,甚至动态计算结果。
-
$this->id、$this->name是最常见用法,但要注意:如果字段不存在或为 null,它就真的不出现——没有默认值 - 关联模型必须显式加载,否则
$this->posts会是空集合或报错;推荐配合$this->whenLoaded('posts')使用,避免未加载时出错 - 嵌套资源要手动 new:比如
'profile' => new ProfileResource($this->whenLoaded('profile')),不能写成'profile' => $this->profile - 日期建议统一转 ISO 格式:
$this->created_at->toISOString(),别依赖模型的$casts或访问器,资源层应独立控制输出
怎么让集合响应带 data 包裹和分页元信息
直接用 UserResource::collection($users) 默认只处理每个项,不加外层 data 或 meta。要统一结构,必须继承 ResourceCollection 并重写 toArray()。
- 生成集合类时加
--collection:例如php artisan make:resource UserCollection --collection - 在
UserCollection的toArray()中手动包裹:return ['data' => $this->collection, 'meta' => $this->resource->withResponse($request)->getMeta()]; - 如果用了 Laravel 的分页(
$users = User::paginate(10)),$this->resource指向的是分页实例,可调用$this->resource->toArray()获取含data、links、meta的完整结构,再按需调整 - 不要在集合资源里写
new UserResource($item)循环——ResourceCollection已自动代理,重复 new 会导致性能浪费
怎么安全地添加条件字段和隐藏敏感数据
资源不是过滤器,它不自动跳过密码、token 等字段。所有字段都靠你显式列出,漏写即隐藏;但写错或逻辑松动,就会意外暴露。
- 用
when()做运行时判断:'is_admin' => $this->when($this->can('admin'), true),比if更简洁且兼容 collection - 用
mergeWhen()批量加字段组:mergeWhen($request->user()->isSuperAdmin(), ['debug_info' => [...]]) - 永远不要在
toArray()里直接返回$this->getAttributes()或$this->toArray()—— 这会把所有模型属性(包括password_hash、remember_token)全吐出来 - 敏感字段如
email_verified_at、updated_at看似无害,但业务语义上可能不该暴露,得一条条核对
为什么 toArray() 里调用 $this->posts 会触发 N+1
因为 $this 是模型实例,$this->posts 是 Eloquent 的动态属性,首次访问会执行一次查询。如果集合里有 100 个用户,每个都访问 $this->posts,就会发出 100 次 SQL。
- 正确做法是控制器中预加载:
User::with('posts')->get(),然后传给资源 - 资源内只用
$this->whenLoaded('posts')判断是否已加载,再决定是否嵌套PostResource::collection(...) - 如果不确定是否预加载,可用
isset($this->relationLoaded['posts'])替代whenLoaded,但更推荐前者——它是 Laravel 官方支持的检测方式 - 注意:即使写了
whenLoaded,若没预加载,字段就为空;文档里不会警告你漏了 with,只能靠日志或慢查询发现
资源类的 toArray() 是纯函数式出口,没有魔法,也没有兜底。它信任你写的每一行代码,也放大你疏忽的每一个细节——尤其是关联加载时机、条件字段边界、以及哪些字段本不该出现在 API 里。
大量免费API接口:立即使用
涵盖生活服务API、金融科技API、企业工商API、等相关的API接口服务。免费API接口可安全、合规地连接上下游,为数据API应用能力赋能!











