You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Laravel关联模型搭配API资源及whenLoaded方法调用报错如何解决

问题复现背景

你希望通过Laravel的API资源层做接口输出建模,这也是业界公认的API输出数据建模的最佳实践,相关代码定义如下:

Customer 模型

public function invoices () {
    return $this->hasMany('\App\Models\Invoice');
}

Invoice 模型

public function customer() {
    return $this->belongsTo('\App\Models\Customer');
}

Customer 资源类

public function toArray($request)
{
    return [
        'id' => $this->id,
        'invoices' => InvoiceResource::collection('invoices')
    ];
}

Invoice 资源类

public function toArray($request)
{
    $customer = $this->whenLoaded('customer');
    return [
      'id' => $this->id,
      'customer' => CustomerResource::collection($this->whenLoaded($customer)),
    ];
}

Customer 控制器

public function index()
{
    $customers = Customer::with(['invoices']);
    return CustomerResource::collection($customers->paginate(50))->response();
}

访问API端点时抛出如下错误:

Error
Call to a member function first() on string

Illuminate\Http\Resources\Json\ResourceCollection::collectResource
vendor/laravel/framework/src/Illuminate/Http/Resources/CollectsResources.php:30

堆栈显示错误起始于控制器中的return CustomerResource::collection($customers->paginate(50))->response();代码行。


错误根因

两处API资源的写法错误直接触发报错:

  1. Customer Resource中给InvoiceResource::collection()传入了字符串'invoices',而非实际的关联数据集合,字符串对象不存在first()方法
  2. Invoice Resource中对customer单关联错误使用了collection()方法,且whenLoaded()参数传递逻辑错误

修复代码

1. 修正Customer Resource

public function toArray($request)
{
    return [
        'id' => $this->id,
        'invoices' => InvoiceResource::collection($this->whenLoaded('invoices'))
    ];
}

2. 修正Invoice Resource

public function toArray($request)
{
    return [
      'id' => $this->id,
      'customer' => new CustomerResource($this->whenLoaded('customer')),
    ];
}

3. 控制器可选优化(代码更简洁)

public function index()
{
    $customers = Customer::with(['invoices'])->paginate(50);
    return CustomerResource::collection($customers);
}

补充说明

  • 只有一对多、多对多这类返回集合的关联,才需要调用资源类的collection()方法
  • 一对一、多对一这类返回单个模型的关联,直接实例化资源类即可
  • 配合whenLoaded()使用可以保证只有预加载了关联才会输出对应字段,避免N+1查询

内容的提问来源于stack exchange,提问作者GrafixMastaMD

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.27 14:06:01