In-memory Tree Shaping
When you've already fetched a flat result, build the tree without extra queries. Every get() on a NodeTrait model returns a NodeCollection, which knows how to assemble itself.
Tip
If you only need to visit each node — not assemble nested children arrays — see Walking Subtrees. Walking is purely in-memory and skips the children-array allocation that toTree() builds.
1. The flat → tree transform
$flat = Category::query()->defaultOrder()->get();
// → Collection of 9 categories ordered by lft:
// Electronics, Computers, Laptops, Desktops, Phones, Android,
// Books, Fiction, Non-fiction
$tree = $flat->toTree();
$tree is a collection of the top-level nodes only, each with its children relation populated recursively:
$tree->count(); // 2 — Electronics, Books
$tree[0]->name; // 'Electronics'
$tree[0]->children->count(); // 2 — Computers, Phones
$tree[0]->children[0]->children->count(); // 2 — Laptops, Desktops
No extra queries fire — toTree() walks the already-loaded collection and rewires the relations by matching each node's parent_id to the keys present in the collection.
2. Flat-with-hierarchy
toFlatTree() returns a single flat collection in depth-first order — handy when you want to render a tree with <ul>-style indenting but don't want to recurse. It only orders the rows; it does not populate the parent / children relations. If you need those on the flat collection (to call ->parent / ->children without lazy loading), call linkNodes() as shown below:
foreach ($flat->toFlatTree() as $node) {
echo str_repeat(' ', $node->depth) . $node->name . "\n";
}
// Electronics
// Computers
// Laptops
// Desktops
// Phones
// Android
// Books
// Fiction
// Non-fiction
3. Subtree shaping
Both toTree() and toFlatTree() accept an optional $root argument when your collection is a subtree, not the whole table. Without it, every node whose parent isn't present in the collection becomes a top-level node — so a partial or filtered fetch returns a forest of all the maximal subtrees it contains, never silently dropping a node whose parent was filtered out. Pass $root to narrow the result to that node's subtree.
// Just Electronics + descendants, shaped as a subtree
$subtree = Category::query()
->whereDescendantOrSelf($electronics->getBounds())
->defaultOrder()
->get()
->toTree($electronics);
4. Linking nodes without restructuring
If you want the relations populated on the original flat collection — e.g. so each node can call ->parent or ->children without lazy loading — but don't want to re-shape into a tree, call linkNodes():
$flat->linkNodes();
$flat[3]->parent->name; // resolved without a query
$flat[0]->children->pluck('name'); // ['Computers', 'Phones']
toTree() calls linkNodes() internally, so the relations are already populated on the tree result too.