Recursive ActiveRecord

I have a single table of categories with joins to itself to allow me to create subcategories. Active Record models are working just fine, so that I am able to do this:

    $categories = Category::find()->where(['name' => 'Root'])->with('children')->asArray()->one();

That gets me one level deep of children. If my categories are 3 levels deep (for example), I have to do something like this:

    $categories = Category::find()->where(['name' => 'Root'])->with('children.children.children')->asArray()->one();

This works just fine and gets me what I need. What I really would like though, is to not have to repeat the word children (as in, “children.children.children”) because I don’t want to have to know or consider ahead of time how deep the category tree might be.

Is there a way to make this work so that I don’t have to know the depth of the tree ahead of time, and also so that I don’t have to do children.children.children.children.children.children if the tree happens to nest 6 levels?

Thanks to a nice post on Stack Overflow, I found a function to handle it so I decided to just keep moving forward. I have a solution that works now. My ActiveRecord call looks like this now:

$categoriesFlat = Category::find()->asArray()->all();

Followed by this:

$categoriesTree = $this->buildTree($categoriesFlat);

The buildTree() function looks like this:

private function buildTree(array $elements, $parentId = 0) {
        $branch = [];

        foreach ($elements as $element) {
            if ($element['parent_id'] == $parentId) {
                $children = $this->buildTree($elements, $element['id']);
                if ($children) {
                    $element['children'] = $children;
                }
                $branch[] = $element;
            }
        }

        return $branch;
    }
4 Likes