Laravel JSON Resource关联集合访问属性报Query\Builder::$id错误
问题根源分析
- 你完全混淆了Laravel API资源体系中两类核心类的用法边界:
- JsonResource:用于格式化单个模型实例,内部可以通过
$this->xxx直接读取对应模型的属性。 - ResourceCollection:是用于批量包裹多个JsonResource的容器,本身不直接处理单个模型的字段,核心作用是遍历集合内的模型项、逐个格式化后返回列表结构。
- JsonResource:用于格式化单个模型实例,内部可以通过
- 你自定义的
InfoCollection完全不符合ResourceCollection的规范:在toArray方法里没有对集合项做遍历处理,反而错误地把集合容器当成单个模型尝试访问属性,自然取不到值。你之前测试直接返回固定值时,输出的info字段也不符合数组列表的预期结构。 - 触发
Undefined property: Illuminate\Database\Query\Builder::$id报错的常见原因:传给InfoCollection::collection()的$this->info不是已加载的模型集合,而是查询构造器实例——通常是因为访问关联时错误给关联方法加了括号(写成$this->info()而非属性方式的$this->info)、或者关联定义有误、没有正确加载关联。
正确实现步骤
1. 创建单个Info对应的资源类
不要直接写自定义Collection类,先创建处理单个Info模型的JsonResource:
// app/Http/Resources/InfoResource.php namespace App\Http\Resources; use Illuminate\Http\Resources\Json\JsonResource; class InfoResource extends JsonResource { public function toArray($request) { return [ 'id' => $this->id, 'product_id' => $this->product_id, 'country_id' => $this->country_id, 'name' => $this->name, 'description' => $this->description, 'country' => [ 'id' => $this->country->id, 'name' => $this->country->name, 'iso_alpha_2' => $this->country->iso_alpha_2, 'flag' => $this->country->flag, ] ]; } }
2. 修改Product资源的关联输出逻辑
JsonResource本身提供了静态方法::collection(),可以自动生成符合规范的资源集合,完全不需要手动写InfoCollection类就能处理多关联的输出:
// ProductResource的toArray方法 public function toArray($request) { return [ 'id' => $this->id, 'sku' => $this->sku, 'slug' => $this->slug, 'type' => $this->type, // 直接用单资源的collection方法包装关联集合即可 'info' => InfoResource::collection($this->info), ]; }
3. (可选)需要自定义集合逻辑时的正确写法
如果确实需要给info集合添加额外元数据(比如总条数、分页信息等),再自定义ResourceCollection,写法必须符合规范:
namespace App\Http\Resources; use Illuminate\Http\Resources\Json\ResourceCollection; class InfoCollection extends ResourceCollection { public function toArray($request) { // 遍历集合内每一个Info模型,传递给单资源类格式化 return [ 'data' => $this->collection->map(fn($infoItem) => InfoResource::make($infoItem))->all(), 'total' => $this->collection->count(), // 自定义的额外元数据 ]; } }
这时候在Product资源里调用自定义集合的写法为:'info' => new InfoCollection($this->info)
排查Builder报错的注意事项
- 确认访问info关联时用属性方式,不要给关联方法加括号:
- 错误写法:
$this->info()—— 返回Builder实例 - 正确写法:
$this->info—— 返回已加载的Info模型集合
- 错误写法:
- 确认Product模型中的info关联定义正确,必须返回关系类实例而非查询构造器:
// Product模型中的正确关联定义 public function info() { return $this->hasMany(Info::class); } - 控制器中调用资源时记得预加载关联,避免N+1查询问题:
// 预加载info及关联的country $product = Product::with(['info.country'])->find($productId); return new ProductResource($product);
内容的提问来源于stack exchange,提问作者Rade Ilijev
相关产品推荐
相关产品推荐

