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资源的写法错误直接触发报错:
- Customer Resource中给
InvoiceResource::collection()传入了字符串'invoices',而非实际的关联数据集合,字符串对象不存在first()方法 - 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
相关产品推荐
相关产品推荐

