Laravel启用preventAccessingMissingAttributes后出现resource属性错误
解决Laravel开启
preventAccessingMissingAttributes()后的资源集合错误 问题描述
在AppServiceProvider.php的boot方法中添加Model::preventAccessingMissingAttributes();后,API返回如下错误:
The attribute [resource] either does not exist or was not retrieved for model [App\Models\Vehicle].
错误出现在使用自定义DefaultCollection返回分页数据的场景,相关代码如下:
DefaultCollection.php
<?php namespace App\Http\Resources; use Illuminate\Http\Request; use Illuminate\Http\Resources\Json\ResourceCollection; class DefaultCollection extends ResourceCollection { /** * The resource collection class to be used. * * @var string */ protected $resourceClass; /** * Set the resource collection class. * * @param string $resourceClass * @return $this */ public function setResourceClass(string $resourceClass) { $this->resourceClass = $resourceClass; return $this; } /** * Customize the pagination information for the resource. * * @param \Illuminate\Http\Request $request * @param array $paginated * @param array $default * @return array */ public function paginationInformation($request, $paginated, $default) { $default['count'] = $default['meta']['total']; unset($default['meta']); return $default; } /** * Transform the resource collection into an array. * * @return array<int|string, mixed> */ public function toArray(Request $request): array { $resourceClass = $this->resourceClass; return [ 'data' => $resourceClass::collection($this->collection), ]; } }
VehicleController.php
public function index() { $vehicles = Vehicle::with(['manufacturer', 'colors']) ->latest() ->paginate(50); $collection = new DefaultCollection($vehicles); $collection->setResourceClass(VehicleResource::class); return $collection; }
问题原因
开启preventAccessingMissingAttributes()后,Laravel会严格禁止访问模型不存在的属性。自定义的DefaultCollection重写了toArray方法,且未正确设置Laravel资源集合所需的$collects属性,导致分页响应逻辑错误地尝试访问模型的resource属性(该属性不存在于Vehicle模型中)。
解决方案
修改DefaultCollection,使用Laravel资源集合的默认toArray逻辑,并通过$collects属性指定要使用的资源类,替代自定义的$resourceClass:
修改后的DefaultCollection.php
<?php namespace App\Http\Resources; use Illuminate\Http\Request; use Illuminate\Http\Resources\Json\ResourceCollection; class DefaultCollection extends ResourceCollection { /** * The resource class that this collection wraps. * * @var string */ public $collects; /** * Set the resource class for the collection. * * @param string $collects * @return $this */ public function setResourceClass(string $collects) { $this->collects = $collects; return $this; } /** * Customize the pagination information for the resource. * * @param \Illuminate\Http\Request $request * @param array $paginated * @param array $default * @return array */ public function paginationInformation($request, $paginated, $default) { $default['count'] = $default['meta']['total']; unset($default['meta']); return $default; } /** * Transform the resource collection into an array. * * @return array<int|string, mixed> */ public function toArray(Request $request): array { // 使用父类默认的toArray逻辑,确保分页响应能正确处理资源转换 return parent::toArray($request); } }
优化控制器调用(可选)
也可以直接在构造函数中传入资源类,省略setResourceClass方法:
// DefaultCollection中添加构造函数 public function __construct($resource, string $collects) { parent::__construct($resource); $this->collects = $collects; } // VehicleController中调用 public function index() { $vehicles = Vehicle::with(['manufacturer', 'colors']) ->latest() ->paginate(50); return new DefaultCollection($vehicles, VehicleResource::class); }
这样修改后,Laravel的分页响应逻辑会通过$collects属性正确指定每个模型对应的资源类,不会再尝试访问模型不存在的resource属性,同时保留了自定义分页信息的功能。
内容的提问来源于stack exchange,提问作者studio-pj
相关产品推荐
相关产品推荐

