如何为Laravel API资源添加信息?关联项目返回id读取报错
解决Laravel API资源返回关联数据时的「Attempt to read property "id" on array」错误
错误原因分析
- 关联定义返回类型错误:
User模型的firstProject方法返回的是查询构建器,而非预加载的模型/集合,导致资源处理时数据格式异常。 - 资源集合接收数据类型不匹配:
UserCollection的with方法中,map->only(['firstProject'])生成嵌套数组集合,而非模型实例集合,传给ProjectCollection后,ProjectResource尝试读取数组的id属性引发错误。 - 未预加载关联:控制器未预加载
firstProject关联,既导致N+1查询问题,也让资源无法正确获取模型实例。
修复步骤
1. 修正firstProject关联定义
将User模型中的关联改为返回单个模型(符合「第一个项目」的语义),或返回带限制的集合:
// app/Models/User.php public function firstProject() { // 推荐:返回单个模型(匹配目标JSON中单个项目的场景) return $this->hasOne(Project::class)->oldest(); // 若需返回集合(比如保留数组格式): // return $this->hasMany(Project::class)->oldest()->limit(1); }
2. 预加载关联并更新控制器查询
在控制器中预加载firstProject,避免N+1查询,确保关联数据被正确加载:
// app/Http/Controllers/UsersController.php $users = User::withCount('projects') ->with('firstProject') // 预加载第一个项目 ->latest() ->get(); return new UserCollection($users);
3. 修正UserResource的relationships结构
按照目标JSON要求,返回包含项目标识的数组:
// app/Http/Resources/UserResource.php public function toArray($request) { return [ "type" => "users", "id" => $this->id, "attributes" => [ "name" => $this->name, "lastname" => $this->lastname, "projects" => $this->whenCounted('projects') ], "relationships" => [ "projects" => [ "data" => $this->whenLoaded('firstProject', function () { // 单个模型场景:用数组包裹资源标识 return [ ProjectResource::make($this->firstProject)->only('id', 'type') ]; // 集合场景:直接映射资源标识 // return ProjectResource::collection($this->firstProject)->map->only('id', 'type'); }) ] ] ]; }
4. 修正UserCollection的with方法生成included数据
收集所有非空的项目模型,用ProjectResource直接生成符合要求的结构:
// app/Http/Resources/UserCollection.php public function with($request) { // 过滤掉无项目的用户,收集有效项目模型 $projects = $this->collection ->pluck('firstProject') ->filter(); return [ 'included' => ProjectResource::collection($projects) ]; }
5. 调整ProjectCollection(可选)
由于目标JSON的included不需要data包裹,ProjectCollection可保持原样,但若仅用于返回完整项目资源,也可以直接使用ProjectResource::collection替代。
内容的提问来源于stack exchange,提问作者DeveloperX
相关产品推荐
相关产品推荐

