Introduction
A modern Laravel implementation of the nested-set model for hierarchical data — strict types throughout, PHPStan level 9, atomic CASE-WHEN mutations, multi-tree scoping, soft-delete cascade, and an opinionated repair toolkit.
Target: PHP 8.3+ / Laravel 11, 12, 13.
$root = Category::create(['name' => 'Root']);
$root->saveAsRoot();
$child = Category::create(['name' => 'Child']);
$child->appendToNode($root)->save();
Category::query()->whereDescendantOf($root->getBounds())->get();
$root->descendants()->orderBy('lft')->get();
$root->refresh()->getSubtreeSize(); // rgt - lft + 1
Why nested set?
The nested-set encoding stores lft and rgt integers on every node so
any subtree, ancestor chain, or descendant set is a single BETWEEN
query — no recursive CTEs, no N+1 loops. The price is that mutations
(insert / move / delete) have to shift many rows to keep the lft/rgt
sequence dense, so it's best suited to read-heavy hierarchies:
category trees, menu structures, org charts, comment threads.
This package executes every shift as a single CASE WHEN UPDATE, so
even a subtree move that touches thousands of rows is one round trip.
What's in this documentation
Start with Installation, then Migration and Model Setup to get a working tree. From there the sidebar groups material by what you're trying to do — insert/move, query, compute aggregates, repair corruption.