
本文详解如何在 Laravel API 资源中安全地嵌套加载自关联模型(如 Category 的 parent),避免 N+1 查询、无限递归和属性访问错误,并通过 whenLoaded() 实现条件式嵌套。
本文详解如何在 laravel api 资源中安全地嵌套加载自关联模型(如 category 的 parent),避免 n+1 查询、无限递归和属性访问错误,并通过 `whenloaded()` 实现条件式嵌套。
在构建树形结构数据(如分类、菜单、组织架构)的 API 时,常需将自关联关系(例如 Category 的 parent 字段指向同模型)以嵌套对象形式返回,而非仅返回父级 ID。但直接在 Resource 中递归调用自身资源极易引发问题:空值访问异常(Attempt to read property "id" on int)、无限嵌套、N+1 查询等。
根本原因在于:原始代码中 $this->parent 是一个整型 ID(数据库字段值),而非已加载的 Eloquent 关系实例;而 new CategoriesResource($this->parent) 尝试将整数当作模型传入,导致资源构造器在访问 $this->id 时失败。
✅ 正确做法分三步:
-
在模型中正确定义自关联关系
确保Category模型使用belongsTo()明确声明parent关系,并指定外键(parent字段)与关联键(id):// Domain/Category/Models/Category.php public function parent(): BelongsTo { return $this->belongsTo(Category::class, 'parent', 'id'); } -
控制器中预加载关系,避免 N+1
在查询时显式with('parent'),确保关系已加载:// Controller public function show(Category $category) { $category->load('parent'); // 或在查询时:Category::with('parent')->find($id) return new CategoriesResource($category); } -
Resource 中使用
whenLoaded()安全嵌套whenLoaded()是 Laravel 资源提供的关键工具——它仅在关系已被预加载(且非 null)时才执行闭包或构造资源,否则返回null,彻底规避空值/类型错误:
Laravel Cloud下载Laravel Cloud(cloud.laravel.com)。此技能用于处理所有 Laravel Cloud 请求,包括搜索和读取数据。只要任务涉及 Laravel Cloud,即使用此技能。
// Domain/Category/Resources/CategoriesResource.php public function toArray($request): array { return [ 'id' => (string) $this->id, 'type' => 'categories', 'attributes' => [ 'name' => $this->name, 'slug' => $this->slug, 'parent' => $this->whenLoaded('parent', fn() => new CategoriesResource($this->parent)), 'description' => $this->description, 'image' => $this->image, 'created_at' => $this->created_at, 'updated_at' => $this->updated_at, ], ]; }
⚠️ 注意事项:
-
绝不递归嵌套:上述方案只加载一级
parent,不会自动加载parent->parent。若需多层(如面包屑),应改用ancestors关系 +with('ancestors')配合CollectionResource,而非在 Resource 内部递归调用。 -
避免循环引用:
CategoriesResource不应在parent属性中再次包含parent字段(即子资源不重复嵌套),本方案天然满足此要求。 -
类型安全增强(可选):可在 Resource 构造器中添加判空逻辑,进一步防御:
'parent' => $this->whenLoaded('parent', function () { return $this->parent ? new CategoriesResource($this->parent) : null; }),
总结:通过「模型关系正确定义 + 控制器预加载 + Resource 条件渲染」三位一体,即可优雅、高效、安全地实现 Laravel 自关联资源的单层嵌套,兼顾 API 响应语义化与系统性能。










