Skip to content

Commit 595f12f

Browse files
docs: enhance README.md with database setup instructions and migration details for single and multiple trees. (#67)
1 parent ac460f5 commit 595f12f

File tree

3 files changed

+116
-27
lines changed

3 files changed

+116
-27
lines changed

README.md

Lines changed: 104 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -59,11 +59,115 @@ composer require yii2-extensions/nested-sets-behavior
5959

6060
### How it works
6161

62+
The nested sets model is a technique for storing hierarchical data in a relational database. Unlike adjacency lists
63+
(parent_id approach), nested sets enable efficient tree operations with minimal database queries.
64+
6265
1. **Creates root nodes** using the nested sets pattern with `lft`, `rgt`, and `depth` fields.
6366
2. **Manages hierarchy** automatically when inserting, moving, or deleting nodes.
6467
3. **Optimizes queries** using boundary values for efficient tree traversal.
6568
4. **Supports transactions** to ensure data integrity during complex operations.
6669

70+
#### Why nested sets?
71+
72+
- **Fast queries**: Get all descendants with a single query (`lft BETWEEN parent.lft AND parent.rgt`).
73+
- **Efficient tree operations**: No recursive queries needed for tree traversal.
74+
- **Automatic maintenance**: Left/right boundaries are calculated automatically.
75+
- **Depth tracking**: Easy to limit query depth or build breadcrumbs.
76+
77+
```text
78+
Example tree structure:
79+
Electronics (1,12,0)
80+
├── Mobile Phones (2,7,1)
81+
│ └── Smartphones (3,6,2)
82+
│ └── iPhone (4,5,3)
83+
└── Computers (8,11,1)
84+
└── Laptops (9,10,2)
85+
86+
Numbers represent: (left, right, depth)
87+
```
88+
89+
### Database setup
90+
91+
The package includes ready-to-use migrations for creating the necessary database structure.
92+
93+
#### Quick setup (Recommended)
94+
95+
1. **Configure console application**:
96+
```php
97+
<?php
98+
99+
declare(strict_types=1);
100+
101+
use yii\console\controllers\MigrateController;
102+
103+
// console/config/main.php
104+
return [
105+
'controllerMap' => [
106+
'migrate' => [
107+
'class' => MigrateController::class,
108+
'migrationPath' => [
109+
'@console/migrations',
110+
'@vendor/yii2-extensions/nested-sets-behavior/migrations',
111+
],
112+
],
113+
],
114+
];
115+
```
116+
117+
2. **Run migrations**:
118+
```bash
119+
# For single tree structure
120+
./yii migrate/up m250707_103609_tree
121+
122+
# For multiple trees structure
123+
./yii migrate/up m250707_104009_multiple_tree
124+
```
125+
126+
#### Alternative: Direct migration execution
127+
128+
```bash
129+
# Run without configuration changes
130+
./yii migrate/up --migrationPath=@vendor/yii2-extensions/nested-sets-behavior/migrations
131+
```
132+
133+
#### Table structures created
134+
135+
**Single tree** (`m250707_103609_tree.php`). Creates a `tree` table for single hierarchical structure.
136+
137+
```sql
138+
CREATE TABLE tree (
139+
id INTEGER NOT NULL PRIMARY KEY,
140+
name VARCHAR(255) NOT NULL,
141+
lft INTEGER NOT NULL,
142+
rgt INTEGER NOT NULL,
143+
depth INTEGER NOT NULL
144+
);
145+
146+
CREATE INDEX idx_tree_lft ON tree (lft);
147+
CREATE INDEX idx_tree_rgt ON tree (rgt);
148+
CREATE INDEX idx_tree_depth ON tree (depth);
149+
CREATE INDEX idx_tree_lft_rgt ON tree (lft, rgt);
150+
```
151+
152+
**Multiple trees** (`m250707_104009_multiple_tree.php`). Creates a `multiple_tree` table for multiple independent trees.
153+
154+
```sql
155+
CREATE TABLE multiple_tree (
156+
id INTEGER NOT NULL PRIMARY KEY,
157+
tree INTEGER DEFAULT NULL,
158+
name VARCHAR(255) NOT NULL,
159+
lft INTEGER NOT NULL,
160+
rgt INTEGER NOT NULL,
161+
depth INTEGER NOT NULL
162+
);
163+
164+
CREATE INDEX idx_multiple_tree_tree ON multiple_tree (tree);
165+
CREATE INDEX idx_multiple_tree_lft ON multiple_tree (lft);
166+
CREATE INDEX idx_multiple_tree_rgt ON multiple_tree (rgt);
167+
CREATE INDEX idx_multiple_tree_depth ON multiple_tree (depth);
168+
CREATE INDEX idx_multiple_tree_tree_lft_rgt ON multiple_tree (tree, lft, rgt);
169+
```
170+
67171
### Basic Configuration
68172

69173
Add the behavior to your ActiveRecord model.
@@ -111,31 +215,6 @@ class Category extends ActiveRecord
111215
}
112216
```
113217

114-
### Database schema
115-
116-
Create the required database fields.
117-
118-
```sql
119-
-- Single tree structure
120-
CREATE TABLE category (
121-
id INT PRIMARY KEY AUTO_INCREMENT,
122-
name VARCHAR(255) NOT NULL,
123-
lft INT NOT NULL,
124-
rgt INT NOT NULL,
125-
depth INT NOT NULL
126-
);
127-
128-
-- Multiple trees structure
129-
CREATE TABLE category (
130-
id INT PRIMARY KEY AUTO_INCREMENT,
131-
tree INT,
132-
name VARCHAR(255) NOT NULL,
133-
lft INT NOT NULL,
134-
rgt INT NOT NULL,
135-
depth INT NOT NULL
136-
);
137-
```
138-
139218
### Basic Usage
140219

141220
#### Creating and building trees

migrations/m250707_103609_tree.php

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,15 +10,20 @@ public function safeUp()
1010
{
1111
$rawPrimaryKey = 'NUMBER GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY';
1212

13+
if ($this->db->driverName === 'mysql') {
14+
$tableOptions = 'ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci';
15+
}
16+
1317
$this->createTable(
1418
'{{%tree}}',
1519
[
1620
'id' => $this->db->driverName !== 'oci' ? $this->primaryKey()->notNull() : $rawPrimaryKey,
17-
'name' => $this->db->driverName === 'oci' ? $this->string()->notNull() : $this->text()->notNull(),
21+
'name' => $this->string(255)->notNull(),
1822
'lft' => $this->integer()->notNull(),
1923
'rgt' => $this->integer()->notNull(),
2024
'depth' => $this->integer()->notNull(),
2125
],
26+
$tableOptions ?? null,
2227
);
2328

2429
$this->createIndex('idx_tree_lft', '{{%tree}}', 'lft');

migrations/m250707_104009_multiple_tree.php

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,16 +10,21 @@ public function safeUp()
1010
{
1111
$rawPrimaryKey = 'NUMBER GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY';
1212

13+
if ($this->db->driverName === 'mysql') {
14+
$tableOptions = 'ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci';
15+
}
16+
1317
$this->createTable(
1418
'{{%multiple_tree}}',
1519
[
1620
'id' => $this->db->driverName !== 'oci' ? $this->primaryKey()->notNull() : $rawPrimaryKey,
1721
'tree' => $this->integer()->null(),
18-
'name' => $this->db->driverName === 'oci' ? $this->string()->notNull() : $this->text()->notNull(),
22+
'name' => $this->string(255)->notNull(),
1923
'lft' => $this->integer()->notNull(),
2024
'rgt' => $this->integer()->notNull(),
2125
'depth' => $this->integer()->notNull(),
2226
],
27+
$tableOptions ?? null,
2328
);
2429

2530
$this->createIndex('idx_multiple_tree_tree', '{{%multiple_tree}}', 'tree');

0 commit comments

Comments
 (0)