Skip to content

Commit ac460f5

Browse files
feat: Add migration files for tree and multiple_tree tables with support for various database drivers. (#66)
1 parent 6d79343 commit ac460f5

File tree

4 files changed

+147
-34
lines changed

4 files changed

+147
-34
lines changed

migrations/m250707_103609_tree.php

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
use yii\db\Migration;
6+
7+
class m250707_103609_tree extends Migration
8+
{
9+
public function safeUp()
10+
{
11+
$rawPrimaryKey = 'NUMBER GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY';
12+
13+
$this->createTable(
14+
'{{%tree}}',
15+
[
16+
'id' => $this->db->driverName !== 'oci' ? $this->primaryKey()->notNull() : $rawPrimaryKey,
17+
'name' => $this->db->driverName === 'oci' ? $this->string()->notNull() : $this->text()->notNull(),
18+
'lft' => $this->integer()->notNull(),
19+
'rgt' => $this->integer()->notNull(),
20+
'depth' => $this->integer()->notNull(),
21+
],
22+
);
23+
24+
$this->createIndex('idx_tree_lft', '{{%tree}}', 'lft');
25+
$this->createIndex('idx_tree_rgt', '{{%tree}}', 'rgt');
26+
$this->createIndex('idx_tree_depth', '{{%tree}}', 'depth');
27+
$this->createIndex('idx_tree_lft_rgt', '{{%tree}}', ['lft', 'rgt']);
28+
29+
if ($this->db->driverName !== 'sqlite') {
30+
$this->addCommentOnTable('{{%tree}}', 'Nested sets tree structure for hierarchical data');
31+
$this->addCommentOnColumn('{{%tree}}', 'id', 'Primary key of the tree node');
32+
$this->addCommentOnColumn('{{%tree}}', 'name', 'Name of the tree node');
33+
$this->addCommentOnColumn('{{%tree}}', 'lft', 'Left boundary of nested set');
34+
$this->addCommentOnColumn('{{%tree}}', 'rgt', 'Right boundary of nested set');
35+
$this->addCommentOnColumn('{{%tree}}', 'depth', 'Node depth in the tree hierarchy');
36+
}
37+
}
38+
39+
public function safeDown()
40+
{
41+
$this->dropTable('{{%tree}}');
42+
}
43+
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
use yii\db\Migration;
6+
7+
class m250707_104009_multiple_tree extends Migration
8+
{
9+
public function safeUp()
10+
{
11+
$rawPrimaryKey = 'NUMBER GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY';
12+
13+
$this->createTable(
14+
'{{%multiple_tree}}',
15+
[
16+
'id' => $this->db->driverName !== 'oci' ? $this->primaryKey()->notNull() : $rawPrimaryKey,
17+
'tree' => $this->integer()->null(),
18+
'name' => $this->db->driverName === 'oci' ? $this->string()->notNull() : $this->text()->notNull(),
19+
'lft' => $this->integer()->notNull(),
20+
'rgt' => $this->integer()->notNull(),
21+
'depth' => $this->integer()->notNull(),
22+
],
23+
);
24+
25+
$this->createIndex('idx_multiple_tree_tree', '{{%multiple_tree}}', 'tree');
26+
$this->createIndex('idx_multiple_tree_lft', '{{%multiple_tree}}', 'lft');
27+
$this->createIndex('idx_multiple_tree_rgt', '{{%multiple_tree}}', 'rgt');
28+
$this->createIndex('idx_multiple_tree_depth', '{{%multiple_tree}}', 'depth');
29+
$this->createIndex('idx_multiple_tree_tree_lft_rgt', '{{%multiple_tree}}', ['tree', 'lft', 'rgt']);
30+
31+
if ($this->db->driverName !== 'sqlite') {
32+
$this->addCommentOnTable('{{%multiple_tree}}', 'Multiple nested sets tree structure for hierarchical data');
33+
$this->addCommentOnColumn('{{%multiple_tree}}', 'tree', 'Tree identifier for multiple trees support');
34+
$this->addCommentOnColumn('{{%multiple_tree}}', 'lft', 'Left boundary of nested set');
35+
$this->addCommentOnColumn('{{%multiple_tree}}', 'rgt', 'Right boundary of nested set');
36+
$this->addCommentOnColumn('{{%multiple_tree}}', 'depth', 'Node depth in the tree hierarchy');
37+
}
38+
}
39+
40+
public function safeDown()
41+
{
42+
$this->dropTable('{{%multiple_tree}}');
43+
}
44+
}

tests/TestCase.php

Lines changed: 43 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,9 @@
99
use Yii;
1010
use yii\base\InvalidArgumentException;
1111
use yii\console\Application;
12-
use yii\db\{ActiveQuery, ActiveRecord, Connection, SchemaBuilderTrait};
12+
use yii\db\{ActiveQuery, ActiveRecord, Connection, Query, SchemaBuilderTrait};
1313
use yii2\extensions\nestedsets\tests\support\model\{MultipleTree, Tree};
14+
use yii2\extensions\nestedsets\tests\support\stub\EchoMigrateController;
1415

1516
use function array_merge;
1617
use function array_values;
@@ -169,44 +170,24 @@ protected function buildFlatXMLDataSet(array $dataSet): string
169170
protected function createDatabase(): void
170171
{
171172
$command = $this->getDb()->createCommand();
173+
$dropTables = [
174+
'migration',
175+
'multiple_tree',
176+
'tree',
177+
];
172178

173-
if ($this->getDb()->getTableSchema('tree', true) !== null) {
174-
$command->dropTable('tree')->execute();
179+
try {
180+
$this->runMigrate('down', ['all']);
181+
} catch (RuntimeException) {
175182
}
176183

177-
if ($this->getDb()->getTableSchema('multiple_tree', true) !== null) {
178-
$command->dropTable('multiple_tree')->execute();
184+
foreach ($dropTables as $table) {
185+
if ($this->getDb()->getTableSchema($table, true) !== null) {
186+
$command->dropTable($table)->execute();
187+
}
179188
}
180189

181-
$primaryKey = $this->driverName === 'oci'
182-
? 'NUMBER GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY'
183-
: $this->primaryKey()->notNull();
184-
$name = $this->driverName === 'oci'
185-
? $this->string()->notNull()
186-
: $this->text()->notNull();
187-
188-
$command->createTable(
189-
'tree',
190-
[
191-
'id' => $primaryKey,
192-
'name' => $name,
193-
'lft' => $this->integer()->notNull(),
194-
'rgt' => $this->integer()->notNull(),
195-
'depth' => $this->integer()->notNull(),
196-
],
197-
)->execute();
198-
199-
$command->createTable(
200-
'multiple_tree',
201-
[
202-
'id' => $primaryKey,
203-
'tree' => $this->integer(),
204-
'name' => $name,
205-
'lft' => $this->integer()->notNull(),
206-
'rgt' => $this->integer()->notNull(),
207-
'depth' => $this->integer()->notNull(),
208-
],
209-
)->execute();
190+
$this->runMigrate('up');
210191
}
211192

212193
/**
@@ -405,6 +386,34 @@ protected function replaceQuotes(string $sql): string
405386
};
406387
}
407388

389+
/**
390+
* @phpstan-param array<array-key, mixed> $params
391+
*/
392+
protected function runMigrate(string $action, array $params = []): mixed
393+
{
394+
$migrate = new EchoMigrateController(
395+
'migrate',
396+
Yii::$app,
397+
[
398+
'migrationPath' => dirname(__DIR__) . '/migrations',
399+
'interactive' => false,
400+
],
401+
);
402+
403+
ob_start();
404+
ob_implicit_flush(false);
405+
406+
$result = $migrate->run($action, $params);
407+
408+
$capture = ob_get_clean();
409+
410+
if (is_int($result) && $result !== 0) {
411+
throw new RuntimeException("Migration '{$action}' failed with code {$result}.\nOutput: {$capture}");
412+
}
413+
414+
return $result;
415+
}
416+
408417
/**
409418
* Applies database updates to tree nodes.
410419
*
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace yii2\extensions\nestedsets\tests\support\stub;
6+
7+
use yii\console\controllers\MigrateController;
8+
9+
final class EchoMigrateController extends MigrateController
10+
{
11+
public function stdout($string)
12+
{
13+
echo $string;
14+
15+
return true;
16+
}
17+
}

0 commit comments

Comments
 (0)