yii2-extensions / nested-sets-behavior
yii2-extension
Fund package maintenance!
terabytesoftw
Requires
- php: >=8.1
- yiisoft/yii2: ^2.0.53|^22
Requires (Dev)
- ext-dom: *
- ext-simplexml: *
- infection/infection: ^0.27|^0.30
- maglnet/composer-require-checker: ^4.1
- php-forge/support: ^0.1
- phpstan/extension-installer: ^1.4
- phpstan/phpstan-strict-rules: ^2.0.3
- phpunit/phpunit: ^10.2
- rector/rector: ^2.1
- symplify/easy-coding-standard: ^12.5
- yii2-extensions/phpstan: ^0.3.0
This package is auto-updated.
Last update: 2025-07-13 13:06:28 UTC
README
Nested sets behavior
A powerful behavior for managing hierarchical data structures using the nested sets pattern in Yii ActiveRecord models.
Efficiently store and query tree structures like categories, menus, organizational charts, and any hierarchical data with high-performance database operations.
Features
- ✅ Efficient Tree Operations - Insert, move, delete nodes with automatic boundary management.
- ✅ Flexible Queries - Find ancestors, descendants, siblings, leaves, and roots.
- ✅ Multiple Trees Support - Manage multiple independent trees in the same table.
- ✅ Query Optimization - Single-query operations for maximum performance.
- ✅ Transaction Safety - All operations are wrapped in database transactions.
- ✅ Validation & Error Handling - Comprehensive validation with clear error messages.
Database support
Quick start
Installation
composer require yii2-extensions/nested-sets-behavior
How it works
The nested sets model is a technique for storing hierarchical data in a relational database. Unlike adjacency lists (parent_id approach), nested sets enable efficient tree operations with minimal database queries.
- Creates root nodes using the nested sets pattern with
lft
,rgt
, anddepth
fields. - Manages hierarchy automatically when inserting, moving, or deleting nodes.
- Optimizes queries using boundary values for efficient tree traversal.
- Supports transactions to ensure data integrity during complex operations.
Why nested sets?
- Fast queries: Get all descendants with a single query (
lft BETWEEN parent.lft AND parent.rgt
). - Efficient tree operations: No recursive queries needed for tree traversal.
- Automatic maintenance: Left/right boundaries are calculated automatically.
- Depth tracking: Easy to limit query depth or build breadcrumbs.
Example tree structure:
Electronics (1,12,0)
├── Mobile Phones (2,7,1)
│ └── Smartphones (3,6,2)
│ └── iPhone (4,5,3)
└── Computers (8,11,1)
└── Laptops (9,10,2)
Numbers represent: (left, right, depth)
Database setup
The package includes ready-to-use migrations for creating the necessary database structure.
Quick setup (Recommended)
- Configure console application:
<?php declare(strict_types=1); use yii\console\controllers\MigrateController; // console/config/main.php return [ 'controllerMap' => [ 'migrate' => [ 'class' => MigrateController::class, 'migrationPath' => [ '@console/migrations', '@vendor/yii2-extensions/nested-sets-behavior/migrations', ], ], ], ];
- Run migrations:
# For single tree structure ./yii migrate/up m250707_103609_tree # For multiple trees structure ./yii migrate/up m250707_104009_multiple_tree
Alternative: Direct migration execution
# Run without configuration changes
./yii migrate/up --migrationPath=@vendor/yii2-extensions/nested-sets-behavior/migrations
Table structures created
Single tree (m250707_103609_tree.php
). Creates a tree
table for single hierarchical structure.
CREATE TABLE tree ( id INTEGER NOT NULL PRIMARY KEY, name VARCHAR(255) NOT NULL, lft INTEGER NOT NULL, rgt INTEGER NOT NULL, depth INTEGER NOT NULL ); CREATE INDEX idx_tree_lft ON tree (lft); CREATE INDEX idx_tree_rgt ON tree (rgt); CREATE INDEX idx_tree_depth ON tree (depth); CREATE INDEX idx_tree_lft_rgt ON tree (lft, rgt);
Multiple trees (m250707_104009_multiple_tree.php
). Creates a multiple_tree
table for multiple independent trees.
CREATE TABLE multiple_tree ( id INTEGER NOT NULL PRIMARY KEY, tree INTEGER DEFAULT NULL, name VARCHAR(255) NOT NULL, lft INTEGER NOT NULL, rgt INTEGER NOT NULL, depth INTEGER NOT NULL ); CREATE INDEX idx_multiple_tree_tree ON multiple_tree (tree); CREATE INDEX idx_multiple_tree_lft ON multiple_tree (lft); CREATE INDEX idx_multiple_tree_rgt ON multiple_tree (rgt); CREATE INDEX idx_multiple_tree_depth ON multiple_tree (depth); CREATE INDEX idx_multiple_tree_tree_lft_rgt ON multiple_tree (tree, lft, rgt);
Basic Configuration
Add the behavior to your ActiveRecord model.
<?php declare(strict_types=1); use yii\db\ActiveRecord; use yii2\extensions\nestedsets\NestedSetsBehavior; /** * @phpstan-property int $depth * @phpstan-property int $id * @phpstan-property int $lft * @phpstan-property int $rgt */ class Category extends ActiveRecord { public static function tableName(): string { return '{{%category}}'; } public function behaviors(): array { return [ 'nestedSets' => [ 'class' => NestedSetsBehavior::class, // 'treeAttribute' => 'tree', // Enable for multiple trees // 'leftAttribute' => 'lft', // Default: 'lft' // 'rightAttribute' => 'rgt', // Default: 'rgt' // 'depthAttribute' => 'depth', // Default: 'depth' ], ]; } public function transactions(): array { return [ self::SCENARIO_DEFAULT => self::OP_ALL, ]; } }
Basic Usage
Creating and building trees
<?php declare(strict_types=1); // Create root node $root = new Category(['name' => 'Electronics']); $root->makeRoot(); // Add children $phones = new Category(['name' => 'Mobile Phones']); $phones->appendTo($root); $computers = new Category(['name' => 'Computers']); $computers->appendTo($root); // Add grandchildren $smartphone = new Category(['name' => 'Smartphones']); $smartphone->appendTo($phones); $laptop = new Category(['name' => 'Laptops']); $laptop->appendTo($computers);
Querying the tree
<?php declare(strict_types=1); // Get all descendants of a node $children = $root->children()->all(); // Get only direct children $directChildren = $root->children(1)->all(); // Get all ancestors of a node $parents = $smartphone->parents()->all(); // Get all leaf nodes (nodes without children) $leaves = $root->leaves()->all(); // Navigate siblings $nextSibling = $phones->next()->one(); $prevSibling = $computers->prev()->one();
Moving nodes
<?php declare(strict_types=1); // Move as last child $smartphone->appendTo($computers); // Move as first child $smartphone->prependTo($phones); // Move as next sibling $smartphone->insertAfter($laptop); // Move as previous sibling $smartphone->insertBefore($laptop); // Make node a new root (multiple trees only) $smartphone->makeRoot();
Deleting nodes
<?php declare(strict_types=1); // Delete node only (children become children of parent) $phones->delete(); // Delete node with all descendants $phones->deleteWithChildren();
Query builder integration
Add query behavior for advanced tree queries.
<?php declare(strict_types=1); use yii\db\ActiveQuery; use yii2\extensions\nestedsets\NestedSetsQueryBehavior; /** * @template T of Category * * @extends ActiveQuery<T> */ class CategoryQuery extends ActiveQuery { public function behaviors(): array { return [ 'nestedSetsQuery' => NestedSetsQueryBehavior::class, ]; } } // In your Category model /** * @phpstan-return CategoryQuery<static> */ public static function find(): CategoryQuery { return new CategoryQuery(static::class); }
Now you can use enhanced queries.
<?php declare(strict_types=1); // Find all root nodes $roots = Category::find()->roots()->all(); // Find all leaf nodes $leaves = Category::find()->leaves()->all();
Documentation
For detailed configuration options and advanced usage.
Quality code
Our social networks
License
Fork
This package is a fork of https://github.com/creocoder/yii2-nested-sets with some corrections.