creocoder/yii2-nested-sets
Yii2 Nested Sets Behavior implementing the Modified Preorder Tree Traversal algorithm for hierarchical data. Adds ActiveRecord behavior and query helpers for inserting, moving, and deleting nodes, with optional multi-tree support via a tree attribute.
Installation Add the package via Composer:
composer require creocoder/yii2-nested-sets
(Note: This is a Yii2 package, but Laravel equivalents like artesaos/seotools or spatie/laravel-activitylog can inspire similar workflows in Laravel.)
Model Integration
In your Laravel model (e.g., Category), attach the behavior:
use Creocoder\NestedSets\NestedSetsBehavior;
class Category extends Model
{
public function behaviors()
{
return [
'tree' => [
'class' => NestedSetsBehavior::class,
'depthAttribute' => 'depth',
],
];
}
}
(Laravel alternative: Use Spatie\Tree\Traits\HasTree or Kalnoy\Nestedset\NodeTrait.)
Database Migration
Add lft (left value) and rgt (right value) columns to your table:
Schema::table('categories', function (Blueprint $table) {
$table->integer('lft')->unsigned();
$table->integer('rgt')->unsigned();
});
First Use Case Insert a root node:
$root = Category::create(['name' => 'Root']);
$root->saveTree(); // Triggers nested set update
Tree Traversal
Fetch a subtree (Laravel equivalent using Spatie\Tree):
$children = Category::where('parent_id', $parent->id)->with('children')->get();
(Nested sets optimize this with lft/rgt range queries.)
Dynamic Sorting
Reorder nodes via drag-and-drop (e.g., with SortableJS):
// After reordering, update positions:
Category::reorder(); // Hypothetical method (use `Spatie\Tree` or manual `lft`/`rgt` recalculation).
Performance-Critical Queries
Use lft/rgt for O(1) subtree checks:
// Find all descendants of a node (Laravel + Nested Sets):
$descendants = Category::where('lft', '>', $node->lft)
->where('rgt', '<', $node->rgt)
->get();
Soft Deletes
Handle soft deletes by adjusting lft/rgt gaps (custom logic required).
saved/deleted to auto-update nested sets:
Category::observe(CategoryObserver::class);
class CategoryObserver {
public function saved(Category $category) {
$category->updateTree(); // Hypothetical (use `Spatie\Tree` or manual recalculation).
}
}
$tree = Category::with('children')->where('parent_id', null)->get()->toTree();
Concurrency Issues
DB::transaction():
DB::transaction(function () {
$node->updateTree(); // Hypothetical (or use `Spatie\Tree`).
});
Performance on Large Trees
lft/rgt for 10,000+ nodes is slow. Batch updates or use a library like Kalnoy/Nestedset.Circular References
$tree = Category::where('lft', '>', $root->lft)
->where('rgt', '<', $root->rgt)
->where('depth', '<', 20) // Safety limit
->get();
Migration Pitfalls
lft/rgt columns are unsigned and indexed:
$table->integer('lft')->unsigned()->index();
$table->integer('rgt')->unsigned()->index();
Invalid Trees: Use this query to validate lft/rgt consistency:
SELECT id, lft, rgt, (rgt - lft + 1) AS width
FROM categories
ORDER BY lft;
(All widths should be odd numbers.)
PHP Errors: Check for missing depthAttribute or invalid parent_id references.
Custom Sorting
Override getTreeQuery() to add custom conditions:
public function getTreeQuery() {
return parent::getTreeQuery()->where('is_active', 1);
}
Alternative Storage
Store lft/rgt in a different column name by configuring the behavior:
'tree' => [
'class' => NestedSetsBehavior::class,
'leftAttribute' => 'left_val',
'rightAttribute' => 'right_val',
],
Laravel-Specific Tips
Spatie\Tree or Kalnoy/Nestedset for modern Laravel support.ActiveRecord with Eloquent and adapt behaviors to observers/traits.How can I help you explore Laravel packages today?