diff --git a/README.md b/README.md
index 00f6d4a..b993cd5 100644
--- a/README.md
+++ b/README.md
@@ -2,7 +2,7 @@
-
Nested sets behavior.
+ Nested sets behavior
@@ -19,25 +19,265 @@
+
+
+
+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.
+
## Quick start
### Installation
```bash
-composer require --prefer-dist yii2-extensions/nested-sets-behavior
+composer require yii2-extensions/nested-sets-behavior
+```
+
+### How it works
+
+1. **Creates root nodes** using the nested sets pattern with `lft`, `rgt`, and `depth` fields.
+2. **Manages hierarchy** automatically when inserting, moving, or deleting nodes.
+3. **Optimizes queries** using boundary values for efficient tree traversal.
+4. **Supports transactions** to ensure data integrity during complex operations.
+
+### Basic Configuration
+
+Add the behavior to your ActiveRecord model.
+
+```php
+ [
+ '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,
+ ];
+ }
+}
+```
+
+### Database schema
+
+Create the required database fields.
+
+```sql
+-- Single tree structure
+CREATE TABLE category (
+ id INT PRIMARY KEY AUTO_INCREMENT,
+ name VARCHAR(255) NOT NULL,
+ lft INT NOT NULL,
+ rgt INT NOT NULL,
+ depth INT NOT NULL
+);
+
+-- Multiple trees structure
+CREATE TABLE category (
+ id INT PRIMARY KEY AUTO_INCREMENT,
+ tree INT,
+ name VARCHAR(255) NOT NULL,
+ lft INT NOT NULL,
+ rgt INT NOT NULL,
+ depth INT NOT NULL
+);
+```
+
+### Basic Usage
+
+#### Creating and building trees
+
+```php
+ '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
+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
+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
+delete();
+
+// Delete node with all descendants
+$phones->deleteWithChildren();
+```
+
+### Query builder integration
+
+Add query behavior for advanced tree queries.
+
+```php
+
+ */
+class CategoryQuery extends ActiveQuery
+{
+ public function behaviors(): array
+ {
+ return [
+ 'nestedSetsQuery' => NestedSetsQueryBehavior::class,
+ ];
+ }
+}
+
+// In your Category model
+/**
+ * @phpstan-return CategoryQuery
+ */
+public static function find(): CategoryQuery
+{
+ return new CategoryQuery(static::class);
+}
+```
+
+Now you can use enhanced queries.
+
+```php
+roots()->all();
+
+// Find all leaf nodes
+$leaves = Category::find()->leaves()->all();
```
## Documentation
For detailed configuration options and advanced usage.
+- 📚 [Installation Guide](docs/installation.md)
+- ⚙️ [Configuration Reference](docs/configuration.md)
+- 💡 [Usage Examples](docs/examples.md)
- 🧪 [Testing Guide](docs/testing.md)
+
## Quality code
[](https://github.com/yii2-extensions/nested-sets-behavior/releases)
diff --git a/docs/configuration.md b/docs/configuration.md
new file mode 100644
index 0000000..1867b00
--- /dev/null
+++ b/docs/configuration.md
@@ -0,0 +1,582 @@
+# Configuration reference
+
+## Overview
+
+This guide covers all configuration options for the Yii Nested Sets Behavior, from basic setup to advanced hierarchical
+data management scenarios.
+
+## Basic configuration
+
+### Minimal setup
+
+```php
+ [
+ 'nestedSets' => NestedSetsBehavior::class,
+ ],
+];
+```
+
+### Standard model configuration
+
+```php
+ [
+ 'class' => NestedSetsBehavior::class,
+ 'leftAttribute' => 'lft',
+ 'rightAttribute' => 'rgt',
+ 'depthAttribute' => 'depth',
+ // 'treeAttribute' => 'tree', // Enable for multiple trees
+ ],
+ ];
+ }
+
+ public function transactions(): array
+ {
+ return [
+ self::SCENARIO_DEFAULT => self::OP_ALL,
+ ];
+ }
+}
+```
+
+## Attribute configuration
+
+### Core nested sets attributes
+
+Configure the database field names used by the behavior.
+
+```php
+'behaviors' => [
+ 'nestedSets' => [
+ 'class' => NestedSetsBehavior::class,
+ 'leftAttribute' => 'lft', // Left boundary field
+ 'rightAttribute' => 'rgt', // Right boundary field
+ 'depthAttribute' => 'depth', // Depth/level field
+ ],
+],
+```
+
+### Custom attribute names
+
+Use custom field names if your database schema differs.
+
+```php
+'behaviors' => [
+ 'nestedSets' => [
+ 'class' => NestedSetsBehavior::class,
+ 'leftAttribute' => 'left_boundary',
+ 'rightAttribute' => 'right_boundary',
+ 'depthAttribute' => 'tree_level',
+ ],
+],
+```
+
+### Multiple trees support
+
+Enable multiple independent trees in the same table.
+
+```php
+'behaviors' => [
+ 'nestedSets' => [
+ 'class' => NestedSetsBehavior::class,
+ 'treeAttribute' => 'tree', // Field to distinguish trees
+ 'leftAttribute' => 'lft',
+ 'rightAttribute' => 'rgt',
+ 'depthAttribute' => 'depth',
+ ],
+],
+```
+
+## Query behavior configuration
+
+Add enhanced query capabilities to your models.
+
+### Basic query behavior
+
+```php
+
+ */
+class CategoryQuery extends ActiveQuery
+{
+ public function behaviors(): array
+ {
+ return [
+ 'nestedSetsQuery' => NestedSetsQueryBehavior::class,
+ ];
+ }
+}
+```
+
+### Integration with model
+
+```php
+
+ */
+ public static function find(): CategoryQuery
+ {
+ return new CategoryQuery(static::class);
+ }
+}
+```
+
+## Database schema configurations
+
+### Single tree schema
+
+For applications with one tree per table.
+
+```sql
+CREATE TABLE category (
+ id INT PRIMARY KEY AUTO_INCREMENT,
+ name VARCHAR(255) NOT NULL,
+ lft INT NOT NULL,
+ rgt INT NOT NULL,
+ depth INT NOT NULL,
+
+ INDEX idx_lft_rgt (lft, rgt),
+ INDEX idx_depth (depth)
+);
+```
+
+### Multiple trees schema
+
+For applications with multiple independent trees.
+
+```sql
+CREATE TABLE category (
+ id INT PRIMARY KEY AUTO_INCREMENT,
+ tree INT,
+ name VARCHAR(255) NOT NULL,
+ lft INT NOT NULL,
+ rgt INT NOT NULL,
+ depth INT NOT NULL,
+
+ INDEX idx_tree_lft_rgt (tree, lft, rgt),
+ INDEX idx_tree_depth (tree, depth)
+);
+```
+
+### Custom schema with different field names
+
+```sql
+CREATE TABLE hierarchy (
+ id INT PRIMARY KEY AUTO_INCREMENT,
+ tree_id INT,
+ title VARCHAR(255) NOT NULL,
+ left_boundary INT NOT NULL,
+ right_boundary INT NOT NULL,
+ tree_level INT NOT NULL,
+
+ INDEX idx_tree_boundaries (tree_id, left_boundary, right_boundary),
+ INDEX idx_tree_level (tree_id, tree_level)
+);
+```
+
+Corresponding behavior configuration.
+
+```php
+'behaviors' => [
+ 'nestedSets' => [
+ 'class' => NestedSetsBehavior::class,
+ 'treeAttribute' => 'tree_id',
+ 'leftAttribute' => 'left_boundary',
+ 'rightAttribute' => 'right_boundary',
+ 'depthAttribute' => 'tree_level',
+ ],
+],
+```
+
+## Transaction configuration
+
+### Enable transactions
+
+Ensure data integrity during tree operations.
+
+```php
+public function transactions(): array
+{
+ return [
+ self::SCENARIO_DEFAULT => self::OP_ALL,
+ ];
+}
+```
+
+### Selective transaction control
+
+Enable transactions only for specific operations.
+
+```php
+public function transactions(): array
+{
+ return [
+ self::SCENARIO_DEFAULT => self::OP_INSERT | self::OP_UPDATE | self::OP_DELETE,
+ ];
+}
+```
+
+### Disable transactions for specific operations
+
+```php
+public function isTransactional($operation): bool
+{
+ if ($operation === ActiveRecord::OP_DELETE) {
+ return false; // Disable transactions for delete operations
+ }
+
+ return parent::isTransactional($operation);
+}
+```
+
+## Validation configuration
+
+### Basic validation rules
+
+```php
+public function rules(): array
+{
+ return [
+ ['name', 'required'],
+ ['name', 'string', 'max' => 255],
+ // IMPORTANT: Do NOT add validation rules for nested sets fields
+ // lft, rgt, depth, tree are managed automatically
+ ];
+}
+```
+
+### Validation with additional fields
+
+```php
+public function rules(): array
+{
+ return [
+ [['name', 'description'], 'required'],
+ ['name', 'string', 'max' => 255],
+ ['description', 'string'],
+ ['status', 'in', 'range' => ['active', 'inactive']],
+ ['sort_order', 'integer', 'min' => 0],
+ // Nested sets fields are excluded from validation
+ ];
+}
+```
+
+## Performance optimization
+
+### Database indexes
+
+Critical indexes for optimal performance.
+
+```sql
+-- Single tree indexes
+CREATE INDEX idx_lft_rgt ON table_name (lft, rgt);
+CREATE INDEX idx_depth ON table_name (depth);
+
+-- Multiple trees indexes
+CREATE INDEX idx_tree_lft_rgt ON table_name (tree, lft, rgt);
+CREATE INDEX idx_tree_depth ON table_name (tree, depth);
+
+-- Composite indexes for complex queries
+CREATE INDEX idx_tree_depth_lft ON table_name (tree, depth, lft);
+```
+
+### Query optimization
+
+Use appropriate query methods for different scenarios.
+
+```php
+// Efficient: Get direct children only
+$directChildren = $node->children(1)->all();
+
+// Less efficient: Get all descendants then filter
+$allDescendants = $node->children()->all();
+$directChildren = array_filter($allDescendants, static fn($child) => $child->depth === $node->depth + 1);
+```
+
+## Advanced configuration
+
+### Custom behavior extension
+
+Extend the behavior for additional functionality.
+
+```php
+getOwner()
+ ->children()
+ ->orderBy([$this->sortAttribute => SORT_ASC])
+ ->all();
+ }
+}
+```
+
+### Event handling
+
+Handle nested sets events in your model.
+
+```php
+public function init(): void
+{
+ parent::init();
+
+ $this->on(ActiveRecord::EVENT_AFTER_INSERT, [$this, 'afterNestedInsert']);
+ $this->on(ActiveRecord::EVENT_AFTER_UPDATE, [$this, 'afterNestedUpdate']);
+ $this->on(ActiveRecord::EVENT_AFTER_DELETE, [$this, 'afterNestedDelete']);
+}
+
+public function afterNestedInsert($event): void
+{
+ // Custom logic after node insertion
+ $this->updateCacheCounters();
+}
+
+public function afterNestedUpdate($event): void
+{
+ // Custom logic after node movement
+ $this->refreshRelatedData();
+}
+
+public function afterNestedDelete($event): void
+{
+ // Custom logic after node deletion
+ $this->cleanupOrphanedData();
+}
+```
+
+### Integration with other behaviors
+
+Combine with other Yii behaviors.
+
+```php
+public function behaviors(): array
+{
+ return [
+ 'timestamp' => [
+ 'class' => TimestampBehavior::class,
+ 'createdAtAttribute' => 'created_at',
+ 'updatedAtAttribute' => 'updated_at',
+ ],
+ 'nestedSets' => [
+ 'class' => NestedSetsBehavior::class,
+ 'treeAttribute' => 'tree',
+ ],
+ 'sluggable' => [
+ 'class' => SluggableBehavior::class,
+ 'attribute' => 'name',
+ ],
+ ];
+}
+```
+
+### Common configuration errors
+
+Avoid these common mistakes:
+
+```php
+// ❌ Wrong: Adding validation for nested sets fields
+public function rules(): array
+{
+ return [
+ ['lft', 'integer'], // Don't validate these fields
+ ['rgt', 'integer'], // They're managed automatically
+ ['depth', 'integer'], // Behavior handles these
+ ['tree', 'integer'],
+ ];
+}
+
+// ✅ Correct: Only validate your business fields
+public function rules(): array
+{
+ return [
+ ['name', 'required'],
+ ['name', 'string', 'max' => 255],
+ // Nested sets fields are excluded
+ ];
+}
+
+// ❌ Wrong: Missing transactions configuration
+public function behaviors(): array
+{
+ return [
+ 'nestedSets' => NestedSetsBehavior::class,
+ // Missing transactions() method
+ ];
+}
+
+// ✅ Correct: Enable transactions for data integrity
+public function behaviors(): array
+{
+ return [
+ 'nestedSets' => NestedSetsBehavior::class,
+ ];
+}
+
+public function transactions(): array
+{
+ return [
+ self::SCENARIO_DEFAULT => self::OP_ALL,
+ ];
+}
+```
+
+## Migration configurations
+
+### Adding nested sets to existing table
+
+Add nested sets fields to an existing table.
+
+```php
+addColumn('{{%category}}', 'lft', $this->integer()->notNull()->defaultValue(1));
+ $this->addColumn('{{%category}}', 'rgt', $this->integer()->notNull()->defaultValue(2));
+ $this->addColumn('{{%category}}', 'depth', $this->integer()->notNull()->defaultValue(0));
+
+ // Add tree column for multiple trees (optional)
+ $this->addColumn('{{%category}}', 'tree', $this->integer());
+
+ // Add performance indexes
+ $this->createIndex('idx_category_lft_rgt', '{{%category}}', ['lft', 'rgt']);
+ $this->createIndex('idx_category_depth', '{{%category}}', ['depth']);
+ $this->createIndex('idx_category_tree', '{{%category}}', ['tree']);
+
+ // Initialize existing records as root nodes
+ $this->execute("
+ UPDATE {{%category}}
+ SET tree = id, lft = 1, rgt = 2, depth = 0
+ WHERE lft IS NULL OR lft = 0
+ ");
+ }
+
+ public function safeDown(): void
+ {
+ $this->dropIndex('idx_category_tree', '{{%category}}');
+ $this->dropIndex('idx_category_depth', '{{%category}}');
+ $this->dropIndex('idx_category_lft_rgt', '{{%category}}');
+
+ $this->dropColumn('{{%category}}', 'tree');
+ $this->dropColumn('{{%category}}', 'depth');
+ $this->dropColumn('{{%category}}', 'rgt');
+ $this->dropColumn('{{%category}}', 'lft');
+ }
+}
+```
+
+### Converting from adjacency list
+
+Migrate from parent_id structure to nested sets.
+
+```php
+addColumn('{{%category}}', 'lft', $this->integer());
+ $this->addColumn('{{%category}}', 'rgt', $this->integer());
+ $this->addColumn('{{%category}}', 'depth', $this->integer());
+
+ // Convert adjacency list to nested sets
+ $this->convertAdjacencyToNestedSets();
+
+ // Make columns NOT NULL after conversion
+ $this->alterColumn('{{%category}}', 'lft', $this->integer()->notNull());
+ $this->alterColumn('{{%category}}', 'rgt', $this->integer()->notNull());
+ $this->alterColumn('{{%category}}', 'depth', $this->integer()->notNull());
+
+ // Add indexes
+ $this->createIndex('idx_category_lft_rgt', '{{%category}}', ['lft', 'rgt']);
+ $this->createIndex('idx_category_depth', '{{%category}}', ['depth']);
+
+ // Drop old parent_id column (optional)
+ // $this->dropColumn('{{%category}}', 'parent_id');
+ }
+
+ private function convertAdjacencyToNestedSets(): void
+ {
+ // This is a simplified conversion - you may need more complex logic
+ $this->execute("
+ -- Set root nodes
+ UPDATE {{%category}}
+ SET lft = 1, rgt = 2, depth = 0
+ WHERE parent_id IS NULL;
+
+ -- You'll need a recursive procedure or application logic
+ -- to properly convert the entire tree structure
+ ");
+ }
+}
+```
+
+## Next steps
+
+- 💡 [Usage Examples](examples.md)
+- 🧪 [Testing Guide](testing.md)
diff --git a/docs/examples.md b/docs/examples.md
new file mode 100644
index 0000000..2db12ef
--- /dev/null
+++ b/docs/examples.md
@@ -0,0 +1,685 @@
+# Usage examples
+
+This document provides comprehensive examples of how to use the Yii Nested Sets Behavior in real-world hierarchical data
+management scenarios.
+
+## Building tree structures
+
+### Creating root nodes
+
+```php
+ 'All Categories']);
+$root->makeRoot();
+
+echo "Root created: ID={$root->id}, Left={$root->lft}, Right={$root->rgt}, Depth={$root->depth}\n";
+// Output: Root created: ID=1, Left=1, Right=2, Depth=0
+```
+
+### Creating multiple tree roots
+
+```php
+ 'Electronics']);
+$electronicsRoot->makeRoot();
+
+$clothingRoot = new Category(['name' => 'Clothing']);
+$clothingRoot->makeRoot();
+
+$homeRoot = new Category(['name' => 'Home & Garden']);
+$homeRoot->makeRoot();
+
+// Each root has its own tree identifier
+echo "Electronics tree: {$electronicsRoot->tree}\n";
+echo "Clothing tree: {$clothingRoot->tree}\n";
+echo "Home tree: {$homeRoot->tree}\n";
+```
+
+### Building a complete category hierarchy
+
+```php
+ 'Store Categories']);
+$root->makeRoot();
+
+// Add main categories
+$electronics = new Category(['name' => 'Electronics']);
+$electronics->appendTo($root);
+
+$clothing = new Category(['name' => 'Clothing']);
+$clothing->appendTo($root);
+
+// Add subcategories to Electronics
+$phones = new Category(['name' => 'Mobile Phones']);
+$phones->appendTo($electronics);
+
+$computers = new Category(['name' => 'Computers']);
+$computers->appendTo($electronics);
+
+// Add sub-subcategories
+$smartphones = new Category(['name' => 'Smartphones']);
+$smartphones->appendTo($phones);
+
+$featurePhones = new Category(['name' => 'Feature Phones']);
+$featurePhones->appendTo($phones);
+
+$laptops = new Category(['name' => 'Laptops']);
+$laptops->appendTo($computers);
+
+$desktops = new Category(['name' => 'Desktop Computers']);
+$desktops->appendTo($computers);
+
+// Result tree structure:
+// Store Categories (1,16)
+// ├── Electronics (2,13)
+// │ ├── Mobile Phones (3,8)
+// │ │ ├── Smartphones (4,5)
+// │ │ └── Feature Phones (6,7)
+// │ └── Computers (9,12)
+// │ ├── Laptops (10,11)
+// │ └── Desktop Computers (12,13)
+// └── Clothing (14,15)
+```
+
+## Inserting and positioning nodes
+
+### Adding nodes as children
+
+```php
+ 'Accessories']);
+$accessories->appendTo($electronics); // Becomes last child
+
+// Prepend as first child
+$tablets = new Category(['name' => 'Tablets']);
+$tablets->prependTo($electronics); // Becomes first child
+
+// Result: Electronics now has Tablets, Mobile Phones, Computers, Accessories
+```
+
+### Inserting nodes as siblings
+
+```php
+ 'Gaming']);
+$gaming->insertAfter($computers); // Places Gaming after Computers
+
+// Insert before a specific node
+$audio = new Category(['name' => 'Audio']);
+$audio->insertBefore($phones); // Places Audio before Mobile Phones
+
+// Result: Electronics now has Audio, Mobile Phones, Computers, Gaming, Accessories
+```
+
+### Positioning nodes with specific ordering
+
+```php
+ 'Electronics']);
+
+ // Create categories in desired order
+ $categories = [
+ 'Computers',
+ 'Mobile Phones',
+ 'Audio',
+ 'Gaming',
+ 'Accessories'
+ ];
+
+ $previousCategory = null;
+
+ foreach ($categories as $categoryName) {
+ $category = new Category(['name' => $categoryName]);
+
+ if ($previousCategory === null) {
+ // First category becomes first child
+ $category->prependTo($electronics);
+ } else {
+ // Subsequent categories inserted after previous
+ $category->insertAfter($previousCategory);
+ }
+
+ $previousCategory = $category;
+ }
+ }
+}
+```
+
+## Querying tree data
+
+### Finding ancestors and descendants
+
+```php
+ 'Smartphones']);
+$breadcrumbs = $smartphone->parents()->all();
+
+foreach ($breadcrumbs as $ancestor) {
+ echo "{$ancestor->name} > ";
+}
+
+echo $smartphone->name;
+// Output: Store Categories > Electronics > Mobile Phones > Smartphones
+
+// Get all descendants of a node
+$electronics = Category::findOne(['name' => 'Electronics']);
+$allDescendants = $electronics->children()->all();
+
+echo "Electronics has " . count($allDescendants) . " descendants:\n";
+
+foreach ($allDescendants as $descendant) {
+ echo str_repeat(' ', $descendant->depth - $electronics->depth) . $descendant->name . "\n";
+}
+```
+
+### Finding direct children and parents
+
+```php
+ 'Electronics']);
+$directChildren = $electronics->children(1)->all();
+
+echo "Direct children of Electronics:\n";
+
+foreach ($directChildren as $child) {
+ echo "- {$child->name}\n";
+}
+
+// Get only direct parent
+$smartphone = Category::findOne(['name' => 'Smartphones']);
+$directParent = $smartphone->parents(1)->one();
+
+echo "Direct parent of {$smartphone->name}: {$directParent->name}\n";
+```
+
+### Finding siblings
+
+```php
+ 'Mobile Phones']);
+$nextSibling = $phones->next()->one();
+
+if ($nextSibling) {
+ echo "Next sibling: {$nextSibling->name}\n";
+}
+
+// Get previous sibling
+$prevSibling = $phones->prev()->one();
+
+if ($prevSibling) {
+ echo "Previous sibling: {$prevSibling->name}\n";
+}
+
+// Get all siblings (including self)
+$allSiblings = $phones->parents(1)->one()->children(1)->all();
+
+echo "All siblings:\n";
+
+foreach ($allSiblings as $sibling) {
+ $current = ($sibling->id === $phones->id) ? ' (current)' : '';
+ echo "- {$sibling->name}{$current}\n";
+}
+```
+
+### Finding leaf nodes
+
+```php
+ 'Electronics']);
+$leaves = $electronics->leaves()->all();
+
+echo "Leaf categories in Electronics:\n";
+
+foreach ($leaves as $leaf) {
+ echo "- {$leaf->name}\n";
+}
+
+// Get all leaf nodes in the entire tree
+$allLeaves = Category::find()->leaves()->all();
+
+echo "All leaf categories:\n";
+
+foreach ($allLeaves as $leaf) {
+ echo "- {$leaf->name}\n";
+}
+```
+
+### Finding root nodes
+
+```php
+roots()->all();
+
+echo "All root categories:\n";
+
+foreach ($roots as $root) {
+ echo "- {$root->name} (Tree ID: {$root->tree})\n";
+}
+```
+
+## Moving and reorganizing nodes
+
+### Moving nodes within the same tree
+
+```php
+ 'Smartphones']);
+$accessories = Category::findOne(['name' => 'Accessories']);
+
+// Move Smartphones to be under Accessories
+$smartphones->appendTo($accessories);
+
+// Move as sibling
+$tablets = Category::findOne(['name' => 'Tablets']);
+$computers = Category::findOne(['name' => 'Computers']);
+
+// Move Tablets to be after Computers
+$tablets->insertAfter($computers);
+```
+
+### Reorganizing tree structure
+
+```php
+ 'Electronics']);
+
+ // Create a new subcategory structure
+ $mobile = new Category(['name' => 'Mobile Devices']);
+ $mobile->appendTo($electronics);
+
+ // Move existing categories under new structure
+ $phones = Category::findOne(['name' => 'Mobile Phones']);
+ $tablets = Category::findOne(['name' => 'Tablets']);
+
+ $phones->appendTo($mobile);
+ $tablets->appendTo($mobile);
+
+ // Result: Electronics > Mobile Devices > (Mobile Phones, Tablets)
+ }
+}
+```
+
+### Moving nodes between trees (multiple trees only)
+
+```php
+ 'Gaming']);
+$entertainmentRoot = Category::findOne(['name' => 'Entertainment']);
+
+// Move Gaming from Electronics tree to Entertainment tree
+$gaming->appendTo($entertainmentRoot);
+
+// The entire Gaming subtree moves to the new tree
+echo "Gaming moved to tree: {$gaming->tree}\n";
+```
+
+### Making existing nodes into new roots
+
+```php
+ 'Gaming']);
+
+// Make Gaming a root of its own tree (multiple trees only)
+$gaming->makeRoot();
+
+echo "Gaming is now root of tree: {$gaming->tree}\n";
+
+// All descendants of Gaming maintain their relative positions
+$gamingChildren = $gaming->children()->all();
+
+foreach ($gamingChildren as $child) {
+ echo "- {$child->name} (Tree: {$child->tree})\n";
+}
+```
+
+## Deleting nodes
+
+### Deleting individual nodes
+
+```php
+ 'Mobile Phones']);
+$children = $mobilePhones->children()->all();
+
+echo "Before deletion, Mobile Phones has " . count($children) . " children\n";
+
+// Delete the node - children become children of Electronics
+$mobilePhones->delete();
+
+// Verify children moved up
+$electronics = Category::findOne(['name' => 'Electronics']);
+$newChildren = $electronics->children(1)->all();
+
+echo "After deletion, Electronics direct children:\n";
+
+foreach ($newChildren as $child) {
+ echo "- {$child->name}\n";
+}
+```
+
+### Deleting nodes with all descendants
+
+```php
+ 'Electronics']);
+$descendantsCount = count($electronics->children()->all());
+
+echo "Electronics has {$descendantsCount} descendants\n";
+
+// Delete Electronics and everything under it
+$deletedCount = $electronics->deleteWithChildren();
+
+echo "Deleted {$deletedCount} nodes total\n";
+```
+
+### Batch deletion with validation
+
+```php
+ false, 'error' => 'Category not found'];
+ }
+
+ // Check if category has children
+ $hasChildren = $category->children()->exists();
+
+ if ($hasChildren && !$deleteChildren) {
+ return [
+ 'success' => false,
+ 'error' => 'Category has children. Specify deleteChildren=true to proceed'
+ ];
+ }
+
+ try {
+ if ($deleteChildren) {
+ $deletedCount = $category->deleteWithChildren();
+
+ return [
+ 'success' => true,
+ 'message' => "Deleted category and {$deletedCount} descendants"
+ ];
+ } else {
+ $category->delete();
+
+ return ['success' => true, 'message' => 'Category deleted'];
+ }
+ } catch (Exception $e) {
+ return ['success' => false, 'error' => $e->getMessage()];
+ }
+ }
+}
+```
+
+## Advanced queries and operations
+
+### Finding nodes by depth level
+
+```php
+ 'Electronics']);
+
+// Get all grandchildren (depth = electronics.depth + 2)
+$grandchildren = $electronics->children(2)
+ ->andWhere(['depth' => $electronics->depth + 2])
+ ->all();
+
+echo "Grandchildren of Electronics:\n";
+
+foreach ($grandchildren as $grandchild) {
+ echo "- {$grandchild->name}\n";
+}
+```
+
+### Building tree menus
+
+```php
+rootId);
+
+ if (!$root) {
+ return '';
+ }
+
+ $tree = $this->buildTreeArray($root);
+
+ return $this->renderTree($tree);
+ }
+
+ private function buildTreeArray($root): array
+ {
+ $children = $root->children($this->maxDepth)->all();
+
+ $tree = [];
+ $currentDepth = $root->depth;
+
+ foreach ($children as $node) {
+ $tree[] = [
+ 'id' => $node->id,
+ 'name' => $node->name,
+ 'depth' => $node->depth - $currentDepth,
+ 'url' => Url::to(['category/view', 'id' => $node->id]),
+ ];
+ }
+
+ return $tree;
+ }
+
+ private function renderTree(array $tree): string
+ {
+ $html = '';
+
+ return $html;
+ }
+}
+```
+
+### Checking node relationships
+
+```php
+isChildOf($ancestor);
+ }
+
+ public function isDescendant(Category $descendant, Category $ancestor): bool
+ {
+ return $descendant->isChildOf($ancestor);
+ }
+
+ public function isSibling(Category $node1, Category $node2): bool
+ {
+ // Nodes are siblings if they have the same direct parent
+ $parent1 = $node1->parents(1)->one();
+ $parent2 = $node2->parents(1)->one();
+
+ return $parent1 && $parent2 && $parent1->id === $parent2->id;
+ }
+
+ public function getCommonAncestor(Category $node1, Category $node2): ?Category
+ {
+ $ancestors1 = $node1->parents()->all();
+ $ancestors2 = $node2->parents()->all();
+
+ // Find common ancestors
+ $commonAncestors = array_intersect(
+ array_column($ancestors1, 'id'),
+ array_column($ancestors2, 'id')
+ );
+
+ if (empty($commonAncestors)) {
+ return null;
+ }
+
+ // Return the deepest common ancestor
+ return Category::findOne(max($commonAncestors));
+ }
+}
+```
+
+### Tree validation and integrity
+
+```php
+andWhere(['tree' => $treeId]);
+ }
+
+ $categories = $query->all();
+
+ foreach ($categories as $category) {
+ // Check left < right
+ if ($category->lft >= $category->rgt) {
+ $errors[] = "Invalid boundaries for {$category->name}: lft={$category->lft}, rgt={$category->rgt}";
+ }
+
+ // Check children boundaries
+ $children = $category->children(1)->all();
+ foreach ($children as $child) {
+ if ($child->lft <= $category->lft || $child->rgt >= $category->rgt) {
+ $errors[] = "Child {$child->name} boundaries invalid relative to parent {$category->name}";
+ }
+
+ if ($child->depth !== $category->depth + 1) {
+ $errors[] = "Child {$child->name} has incorrect depth";
+ }
+ }
+ }
+
+ return $errors;
+ }
+}
+```
+
+This comprehensive examples guide demonstrates practical usage patterns for the Yii Nested Sets Behavior across different
+scenarios, from basic tree building to complex hierarchical data management in real-world applications.
+
+## Next steps
+
+- 📚 [Installation Guide](installation.md)
+- ⚙️ [Configuration Guide](configuration.md)
+- 🧪 [Testing Guide](testing.md)
diff --git a/docs/installation.md b/docs/installation.md
new file mode 100644
index 0000000..f03f535
--- /dev/null
+++ b/docs/installation.md
@@ -0,0 +1,42 @@
+# Installation guide
+
+## System requirements
+
+- [`PHP`](https://www.php.net/downloads) 8.1 or higher.
+- [`Yii2`](https://github.com/yiisoft/yii2) 2.0.53+ or 22.x.
+
+## Installation
+
+### Method 1: Using [Composer](https://getcomposer.org/download/) (recommended)
+
+Install the extension.
+
+```bash
+composer require yii2-extensions/nested-sets-behavior
+```
+
+### Method 2: Manual installation
+
+Add to your `composer.json`.
+
+```json
+{
+ "require": {
+ "yii2-extensions/nested-sets-behavior": "^0.1"
+ }
+}
+```
+
+Then run.
+
+```bash
+composer update
+```
+
+## Next steps
+
+Once the installation is complete.
+
+- ⚙️ [Configuration Reference](configuration.md)
+- 💡 [Usage Examples](examples.md)
+- 🧪 [Testing Guide](testing.md)
diff --git a/docs/testing.md b/docs/testing.md
index 46a4946..57eac39 100644
--- a/docs/testing.md
+++ b/docs/testing.md
@@ -4,23 +4,40 @@
This package uses [composer-require-checker](https://github.com/maglnet/ComposerRequireChecker) to check if all dependencies are correctly defined in `composer.json`.
-To run the checker, execute the following command:
+To run the checker, execute the following command.
```shell
composer run check-dependencies
```
+## Easy coding standard
+
+The code is checked with [Easy Coding Standard](https://github.com/easy-coding-standard/easy-coding-standard) and
+[PHP CS Fixer](https://github.com/PHP-CS-Fixer/PHP-CS-Fixer). To run it.
+
+```shell
+composer run ecs
+```
+
+## Mutation testing
+
+Mutation testing is checked with [Infection](https://infection.github.io/). To run it.
+
+```shell
+composer run mutation
+```
+
## Static analysis
-The code is statically analyzed with [Phpstan](https://phpstan.org/). To run static analysis:
+The code is statically analyzed with [Phpstan](https://phpstan.org/). To run static analysis.
```shell
-composer run phpstan
+composer run static
```
## Unit tests
-The code is tested with [PHPUnit](https://phpunit.de/). To run tests:
+The code is tested with [PHPUnit](https://phpunit.de/). To run tests.
```
composer run test
diff --git a/tests/support/model/ExtendableMultipleTree.php b/tests/support/model/ExtendableMultipleTree.php
index 09ce8f6..55cfb5e 100644
--- a/tests/support/model/ExtendableMultipleTree.php
+++ b/tests/support/model/ExtendableMultipleTree.php
@@ -8,12 +8,12 @@
use yii2\extensions\nestedsets\tests\support\stub\ExtendableNestedSetsBehavior;
/**
- * @property int $id
- * @property int $depth
- * @property int $lft
- * @property int $rgt
- * @property int $tree
- * @property string $name
+ * @phpstan-property int $depth
+ * @phpstan-property int $id
+ * @phpstan-property int $lft
+ * @phpstan-property int $rgt
+ * @phpstan-property int $tree
+ * @phpstan-property string $name
*/
class ExtendableMultipleTree extends ActiveRecord
{
diff --git a/tests/support/model/MultipleTree.php b/tests/support/model/MultipleTree.php
index 72c312a..66b01a0 100644
--- a/tests/support/model/MultipleTree.php
+++ b/tests/support/model/MultipleTree.php
@@ -8,12 +8,12 @@
use yii2\extensions\nestedsets\NestedSetsBehavior;
/**
- * @property int $id
- * @property int $depth
- * @property int $lft
- * @property int $rgt
- * @property int $tree
- * @property string $name
+ * @phpstan-property int $depth
+ * @phpstan-property int $id
+ * @phpstan-property int $lft
+ * @phpstan-property int $rgt
+ * @phpstan-property int $tree
+ * @phpstan-property string $name
*/
class MultipleTree extends ActiveRecord
{
diff --git a/tests/support/model/Tree.php b/tests/support/model/Tree.php
index 8792953..b52abf1 100644
--- a/tests/support/model/Tree.php
+++ b/tests/support/model/Tree.php
@@ -8,11 +8,11 @@
use yii2\extensions\nestedsets\NestedSetsBehavior;
/**
- * @property int $id
- * @property int $lft
- * @property int $rgt
- * @property int $depth
- * @property string $name
+ * @phpstan-property int $depth
+ * @phpstan-property int $id
+ * @phpstan-property int $lft
+ * @phpstan-property int $rgt
+ * @phpstan-property string $name
*/
class Tree extends ActiveRecord
{
diff --git a/tests/support/model/TreeWithStrictValidation.php b/tests/support/model/TreeWithStrictValidation.php
index 8f61831..ba92a34 100644
--- a/tests/support/model/TreeWithStrictValidation.php
+++ b/tests/support/model/TreeWithStrictValidation.php
@@ -5,11 +5,11 @@
namespace yii2\extensions\nestedsets\tests\support\model;
/**
- * @property int $id
- * @property int $lft
- * @property int $rgt
- * @property int $depth
- * @property string $name
+ * @phpstan-property int $depth
+ * @phpstan-property int $id
+ * @phpstan-property int $lft
+ * @phpstan-property int $rgt
+ * @phpstan-property string $name
*/
final class TreeWithStrictValidation extends Tree
{