You need to enable JavaScript to run this app. Laravel JSON Resource关联集合访问属性报Query\Builder::$id错误
优惠活动
大模型
产品
解决方案
定价
更多

Laravel JSON Resource关联集合访问属性报Query\Builder::$id错误

问题根源分析
  • 你完全混淆了Laravel API资源体系中两类核心类的用法边界:
    • JsonResource:用于格式化单个模型实例,内部可以通过$this->xxx直接读取对应模型的属性。
    • ResourceCollection:是用于批量包裹多个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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.24 16:36:01