Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

NoorDeen's avatar

Eloquent. Infinite children into usable array and render it to nested ul

I Have categories table where the category can belongs to other category and category has many categories too . and this for all categories .

  • what I need is to read the categories from the root categories where parent_id = 0 to all the children of this root categories as multi denominational associative array .

example:

$categories = [
    [
        'name' => 'cat1',
        'children' => [
                [
                'name' => 'cat1',
                'children' => [
                    [
                        'name'=>'subcat1',
                        'children'=> []
                    ]
                ]
            ]
        ]
    ]
]
  • how to render it to html nested unordered lists too ?
0 likes
20 replies
pmall's avatar

I guess, without performing one query per depth level right ? :)

If not it is just basic recursion.

1 like
bobbybouwmann's avatar

Yea, it is basic recursion ;)

Take a look at this post: https://laracasts.com/discuss/channels/general-discussion/filesystem-list-directories#reply-34287

Another example

function buildTree(array $elements, $parentId = 0) {
    $branch = array();  
    
    foreach ($elements as $element) {
        if ($element['parent_id'] == $parentId) {
            $children = buildTree($elements, $element['id']);
            
            if ($children) {
                $element['children'] = $children;
            }
            
            $branch[] = $element;
        }
    }
    
    return $branch;
}

$tree = buildTree($rows);
3 likes
Mehdi_Souihed's avatar

The most straightforward however maybe not fastest way is to use a array_walk_recursive function to traverse your array (which is called a tree datastructure). Here is the snippet from the PHP doc adapted to your case :


function test_print($item, $key)
{
    echo "Category name : $item\n";
}

array_walk_recursive($categories, 'test_print');

Another method would be to use the Recursive Iterator however I think in your case it is overkill.

Then to print it to an unordered list you have a Laravel HTML helper, if you use Laravel 5 you will need to install illuminate

Pass your array to your view and print it with :

  {!! HTML::ul($categories) !!}
pmall's avatar
pmall
Best Answer
Level 56

@Mehdi_Souihed where do you get the children here ?

I'll go with :

function list_categories(Array $categories)
{
  $data = [];

  foreach($categories as $category)
  {
    $data[] = [
      'name' = $category->name,
      'children' = list_categories($category->children),
    ];
  }

  return $data;
}

$array_categories = list_categories($root_categories);

Maybe @JarekTkaczyk has a trick to eager load everything ;)

3 likes
JarekTkaczyk's avatar

@DMA You have an error in the parent relation:

    public function parent()
    {
        return $this->hasOne('App\Product', 'id', 'parent_id');
        // should be
        return $this->hasOne('App\Product', 'parent_id', 'id'); // or better without 'id'
    }

A just had a quick look, but tell me - how many queries it requires?

Mehdi_Souihed's avatar

@pmall From his question @AlnourAltegani is only looking for the values of his elements :

I have just tested the following code (I have normalised a bit the array which contained uneeded extra levels)

$categories = array(
        'name' => 'cat1',
        'children' => array (
                
                'name' => 'cat1',
                'children' => array (
                    
                     'name'=>'subcat1',
                     'children'=> array ()
                )
            )
);


function test_print($item, $key)
{
    echo "Category name : $item\n";
}

array_walk_recursive($categories, 'test_print');

Which outputed :

Category name : cat1
Category name : cat1
Category name : subcat1
1 like
DMA's avatar

@JarekTkaczyk it just needs one query, to get all products (in the example). With that it fetches all of the products, categories or whatever, along with corresponding parent_ids and then the rest is done in PHP.

NoorDeen's avatar

@pmall yes if there any way to do it .

@blackbird yes it is simple recursion . but this means if I used @pmall it will run at least 2 queries in every recursion and this will make bad performance .

bobbybouwmann's avatar

I updated my answer, It's most simple way you can think of! And yea using a package makes it much easier, but does he really need it?

NoorDeen's avatar

@DMA Do your package run query every time there is children element or run all queries at once and render it latter ?

Mehdi_Souihed's avatar

@AlnourAltegani @pmall I see, this is at the database level ;-)

I came across that problem for a hierarchy of users. I have used Baum to manage the hierarchies for me.

For your case I guess you'll need to keep querying the database recursively.

1 like
JarekTkaczyk's avatar

@AlnourAltegani Of course you don't need to query the db for each level. Here's an excerpt from a package:

<?php

use Illuminate\Database\Eloquent\Collection;

class Tree extends Collection {

    /**
     * Build tree structure from the collection.
     * 
     * @return static
     */
    public function buildTree()
    {
        $structure = $this->matchNodes();

        $this->clean($structure);

        return $structure;
    }

    /**
     * Match child nodes with the parents.
     * 
     * @return static
     */
    protected function matchNodes()
    {
        $structure = new static($this->items);

        foreach ($this->items as $key => $node)
        {
            $parentId = $node->getParentId();
            $nodeId   = $node->getKey();

            if ($parentId)
            {
                $parent = $structure->find($parentId)
                    ->subtree
                    ->add($node);
            }
        }

        return $structure;
    }

    /**
     * Clean root level of the structure.
     *
     * @param  static $structure
     * @return void
     */
    protected function clean($structure)
    {
        foreach ($structure as $key => $node)
        {
            if ($node->getParentId())
            {
                $structure->forget($key);
            }
        }
    }
...
}
<?php

class Node extends \Eloquent { // might be used as a trait as well

    protected $subtree;

    public function getSubtreeAttribute()
    {
        return $this->subtree = $this->subtree ?: $this->newCollection();
    }

    public function newCollection(array $models = array())
    {
        return new Tree($models);
    }
...
}

Then all you need is this:

$tree = Category::all()->buildTree();
$tree->toArray();
// nice tree structure of your category objects

@DMA I will take a look at your code, I wonder how you tackled this.

3 likes
DMA's avatar

@AlnourAltegani it runs one query. My code is basically just a helper, to output the existing data that has been passed to it.

DMA's avatar

@JarekTkaczyk mine is doing pretty much the exact same thing as the code you've posted :)

1 like
willvincent's avatar

You might also want to look at a pre-order tree traversal solution, like this: http://www.sitepoint.com/hierarchical-data-database-2/

The big drag to that method though is whenever an item is added, removed, or moved around the tree most of the rows will need an update. The upside is, for reads you can read and build your tree very quickly.

Ozan's avatar

@JarekTkaczyk Can you also explain a bit about the code you posted. I wonder how you designed the database too.

Please or to participate in this conversation.