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

Laravel Category资源嵌套自关联实现及循环避免问题咨询

分类资源嵌套父分类及解决无限循环问题

问题背景

现有Category资源和模型,需要将接口返回中的父分类ID替换为嵌套的父分类资源结构,但修改后出现Attempt to read property "id" on int错误,同时需要避免父分类继续返回其自身父分类导致的无限循环。

原始资源代码

<?php

namespace Domain\Category\Resources;

use Illuminate\Http\Request;
use Illuminate\Http\Resources\Json\JsonResource;

class CategoriesResource extends JsonResource
{
    public function toArray($request): array
    {
        return [
            'id' => (string)$this->id,
            'type' => 'categories',
            'attributes' => [
                'name' => $this->name,
                'slug' => $this->slug,
                'parent' => $this->parent,
                'description' => $this->description,
                'image' => $this->image,
                'created_at' => $this->created_at,
                'updated_at' => $this->updated_at,
            ]
        ];
    }
}

错误的模型关联代码

<?php

namespace Domain\Category\Models;

use Domain\Product\Models\Product;
use Domain\Shared\BaseModel;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Support\Str;

class Category extends BaseModel
{
    protected $fillable = [
        'name',
        'parent',
        'description',
        'image',
    ];

    // 方法名与数据库字段名冲突
    public function parent() : BelongsTo
    {
        return $this->belongsTo(Category::class, 'parent');
    }
}

修改后的资源(引发错误)

public function toArray($request): array
{
    return [
        'id' => (string)$this->id,
        'type' => 'categories',
        'attributes' => [
            'name' => $this->name,
            'slug' => $this->slug,
            'parent' => new CategoriesResource($this->parent),
            'description' => $this->description,
            'image' => $this->image,
            'created_at' => $this->created_at,
            'updated_at' => $this->updated_at,
        ]
    ];
}

错误信息

Attempt to read property "id" on int


解决方案

1. 修复"Attempt to read property 'id' on int"错误

核心原因:模型的关联方法名parent与数据库字段名parent完全一致,Eloquent会优先返回数据库字段的整数值,而非关联模型实例,导致将整数传入CategoriesResource时,资源尝试读取id属性引发错误。

解决步骤:
修改模型中的关联方法名,避免与字段名冲突,例如改为parentCategory:

<?php

namespace Domain\Category\Models;

use Domain\Product\Models\Product;
use Domain\Shared\BaseModel;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Support\Str;

class Category extends BaseModel
{
    protected $fillable = [
        'name',
        'parent',
        'description',
        'image',
    ];

    // 修改关联方法名,避免字段名冲突
    public function parentCategory() : BelongsTo
    {
        return $this->belongsTo(Category::class, 'parent');
    }
}

2. 实现嵌套父分类并避免无限循环

有两种常用方式实现需求,同时避免无限循环:

方式一:在主资源中直接控制父分类返回结构

在资源中使用whenLoaded判断关联是否加载,同时为父分类返回不包含parent字段的简化结构:

public function toArray($request): array
{
    return [
        'id' => (string)$this->id,
        'type' => 'categories',
        'attributes' => [
            'name' => $this->name,
            'slug' => $this->slug,
            // 仅当关联加载时返回嵌套结构,否则返回原始ID
            'parent' => $this->whenLoaded('parentCategory', function () {
                return [
                    'id' => (string)$this->parentCategory->id,
                    'type' => 'categories',
                    'attributes' => [
                        'name' => $this->parentCategory->name,
                        'slug' => $this->parentCategory->slug,
                        'description' => $this->parentCategory->description,
                        'image' => $this->parentCategory->image,
                        'created_at' => $this->parentCategory->created_at,
                        'updated_at' => $this->parentCategory->updated_at,
                        // 不返回parent字段,避免无限循环
                    ]
                ];
            }, $this->parent),
            'description' => $this->description,
            'image' => $this->image,
            'created_at' => $this->created_at,
            'updated_at' => $this->updated_at,
        ]
    ];
}

方式二:创建单独的父分类资源类

创建一个不包含parent字段的简化资源类,专门用于返回父分类数据:

// Domain\Category\Resources\ParentCategoryResource.php
<?php

namespace Domain\Category\Resources;

use Illuminate\Http\Request;
use Illuminate\Http\Resources\Json\JsonResource;

class ParentCategoryResource extends JsonResource
{
    public function toArray($request): array
    {
        return [
            'id' => (string)$this->id,
            'type' => 'categories',
            'attributes' => [
                'name' => $this->name,
                'slug' => $this->slug,
                'description' => $this->description,
                'image' => $this->image,
                'created_at' => $this->created_at,
                'updated_at' => $this->updated_at,
                // 省略parent字段,彻底避免循环
            ]
        ];
    }
}

然后在主资源中调用该简化资源:

public function toArray($request): array
{
    return [
        'id' => (string)$this->id,
        'type' => 'categories',
        'attributes' => [
            'name' => $this->name,
            'slug' => $this->slug,
            'parent' => $this->whenLoaded('parentCategory', function () {
                return new ParentCategoryResource($this->parentCategory);
            }, $this->parent),
            'description' => $this->description,
            'image' => $this->image,
            'created_at' => $this->created_at,
            'updated_at' => $this->updated_at,
        ]
    ];
}

3. 预加载关联避免N+1查询

在控制器查询分类时,务必预加载parentCategory关联,避免数据库性能问题:

// 控制器示例代码
$category = Category::with('parentCategory')->find($id);
return new CategoriesResource($category);

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.19 14:55:19