How to make multi-level (nested) categories in Strapi v3?

How do I correctly build a nested list of categories so I could use it in frontend with <select> box element ?

Collection Type "Categories" have following fields:

  • categories.id
  • categories.name
  • categories.category

the handler is in /api/categories/controllers/categories.js:

  async nested(ctx) {
    let entities = await strapi.services.categories.find(ctx.query);

    const cleaned = entities.map(entity => {
      const item = sanitizeEntity(entity, { model: strapi.models.categories });

      return item;
    })

    return nestChilds(cleaned);
  }
function nestChilds (object) {
  const list = [];
  object.forEach(i => {
    console.log('LIST:', list);
    if (!i.parent) {
      list.push(i);
    } else {
      const parent = list.find(x => x.id === i.parent.id);
      parent.childs = [];
      parent.childs.push(i);
    }
  })
  return list;
}

But it's not working. I would like to have some sort of these results:

[
  {
    id: 1,
    name: "Top-level category",
    childs: [
      {
        id: 2,
        name: "2nd level category 1"
      },
      {
        id: 3,
        name: "2nd level category 2",
        childs: [
          {
            id: 5,
            name: "3rd level category 1"
          }
        ]
      },
      {
        id: 4,
        name: "2nd level category 3"
      }
    ]
  }
]

Is there any solution or maybe someone could kickstart me with an idea ?


Solution 1:

So I came up with the following solution:

function nestChilds (object) {
  const list = [];

  object.forEach(i => {
    // If is related to parent
    if (i.parent) {
      // Scope that parent
      const parent = object.find(({ id }) => id === i.parent.id);
      
      // Add a child array property to that parent if not already done
      if (!parent.hasOwnProperty('childs')) parent.childs = [];

      // Add current item to it's corresponding parent
      parent.childs.push(i);

      // Remove parent property
      delete i.parent;
    } else {
      list.push(i);
    }
  })
  return list;
}