4

I use Laravel 6.x and below is my response JSON.

{
    "data": [
        {
            "id": 1,
            "name": "quam",
            "parent_id": 0
        },
        {
            "id": 2,
            "name": "quia",
            "parent_id": 1
        },
        {
            "id": 3,
            "name": "beatae",
            "parent_id": 1
        },
        {
            "id": 4,
            "name": "aut",
            "parent_id": 2
        },
        {
            "id": 5,
            "name": "provident",
            "parent_id": 0
        },
        {
            "id": 6,
            "name": "voluptate",
            "parent_id": 0
        },
        {
            "id": 7,
            "name": "vel",
            "parent_id": 2
        },
        {
            "id": 8,
            "name": "sed",
            "parent_id": 3
        },
        {
            "id": 9,
            "name": "voluptates",
            "parent_id": 0
        },
        {
            "id": 10,
            "name": "adipisci",
            "parent_id": 6
        },
        ...
    ]
}

But it want to be like this:

{
    "data": [
        {
            "id": 1,
            "name": "quam",
            "children": [
                {
                   "id": 2,
                   "name": "quam"
                   "children":[
                       {
                           "id": 4,
                           "name": "aut"
                       },
                       {
                           "id": 7,
                           "name": "vel",
                           "children": [
                                ...
                           ]
                       }
                   ]
                 },
                 {
                   "id": 3,
                   "name": "quam",
                   "children":[
                       {
                           "id": 8,
                           "name": "sed"
                       }
                   ]
                 },
            ]
        },
        {
            "id": 5,
            "name": "provident"
        },
        {
            "id": 6,
            "name": "voluptate",
            "children": [
                 {
                      "id": 10,
                       "name": "adipisci"
                 }
            ]
        },
        {
            "id": 9,
            "name": "voluptates"
        },
        ...
}

In fact, I want to remove the parent_id attribute and add children array to each object that consists of other objects have this parent_id.

CategoryResource.php

class CategoryResource extends JsonResource
{
    public function toArray($request)
    {
        return [
            'id' => $this->id,
            'name' => $this->name,
            'parent_id' => $this->parent_id,
        ];
    }
}

CategoryController.php

class CategoryController extends Controller
{
    public function index()
    {
        return CategoryResource::collection(Category::all());
    }
}

How can I implement this structure?

2
  • with infinite ressources. what i'm saying is, you need to set some limits or you wont be able to anticipate the cost. Commented Dec 17, 2019 at 14:13
  • Presented input array can't be transformed properly into tree-array Commented Dec 17, 2019 at 14:13

3 Answers 3

8

From what I see your problem is just the relations. To create a "tree resource" you have to load a lot of relations.

IMHO it's not a good choice, expecially if you have to load a lot of elements but generally, structures like these may be a dangerous bottleneck.

Anyway... The easy way it's the eager loading, so you have to add your base model with this attribute (have a look at the official documentation)

class Parent extends Model {

  // [...]

  /**
   * The relationships that should always be loaded.
   *
   * @var array
   */
  protected $with = ['children'];

  // [...]

  public function children() {
     return $this->hasMany('whatever');
  }
}

next you have to update your JSON Resource as follows (also for this, have a look at the official documentation about resource relationships).

class CategoryResource extends JsonResource
{

    public function toArray($request)
    {
        return [
            'id' => $this->id,
            'name' => $this->name,
            'parent_it' => $this->parent_id,
            'childrend' => ChildrenResource::collection($this->whenLoaded('children')),
        ];
    }
}

In this way, since everytime you request a Parent it will eager load its children, the resource will recursively map into a Child resource each relation down to the leaf.

Sign up to request clarification or add additional context in comments.

1 Comment

It worked for me after changing ChildrenResource into CategoryResource or Self. thank you
3

Assuming Category is an eloquent model, Model's can reference themselves in relationships and those relationships can be recursive.


class Category extends Model
{

    protected $hidden = ['parent_id'];

    public function children()
    {
        return $this->hasMany('App\Category', 'parent_id')->with('children');
    }
}

So now getting the structure you want is as simple as

Category::with('children:id,name,parent_id')->get('id', 'name', 'parent_id');

You have to include the parent_id in the select statement in order for the relationships to work but the $hidden variable I added to the model keeps parent_id from showing up in serialized results. The only caveat here is that all categories will have a children property, which will be empty for Categories that don't have children. So in your toArray method you will have to check for empty children[] and exclude them

Comments

0

First you need to define a relation for retrieving children of the main category which has no parents with this method

/**
 * get sub product categories of this category
 *
 * @return mixed
 */
public function childCategories()
{
    return $this->hasMany(Category::class,'parent_id');
}

Then you need to load the children of children categories with this method :

/**
 * get recursive all sub categories of this category.
 *
 * @return mixed
 */
public function childrenCategories()
{
    return $this->hasMany(Category::class,'parent_id')->with('childCategories');
}

Now you can retrieve them with this static function

/**
 * get all Main categories with children.
 *
 * @return mixed
 */
public static function allMainCategoriesWithChildren()
{
    return self::whereNull('parent_id')->with('childrenCategories')->get();
}

now you can use it in your resource or return it in controller directly

use App\Category;
return Category::allMainCategoriesWithChildren();

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.