Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 7 additions & 5 deletions src/NestedSetsQueryBehavior.php
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ public function leaves(): ActiveQuery
*
* Selects nodes where the left attribute equals `1`, indicating root nodes in the hierarchical structure.
*
* If the model defines a primary key, the result is ordered by that attribute in ascending order.
* The result is ordered by the left attribute, and if the tree attribute is enabled, by the tree attribute as well.
*
* This method is useful for efficiently fetching all root nodes in single-tree or multi-tree scenarios managed by
* the nested sets pattern.
Expand All @@ -105,14 +105,16 @@ public function roots(): ActiveQuery
{
$class = $this->getOwner()->modelClass;
$model = new $class();
$sortAttribute = $model::primaryKey()[0] ?? null;

$activeQuery = $this->getOwner()->andWhere([$model->leftAttribute => 1]);
$columns = [$model->leftAttribute => SORT_ASC];

if ($sortAttribute !== null) {
$activeQuery->addOrderBy([$sortAttribute => SORT_ASC]);
if ($model->treeAttribute !== false) {
$columns = [$model->treeAttribute => SORT_ASC] + $columns;
}

$activeQuery = $this->getOwner()->andWhere([$model->leftAttribute => 1]);
$activeQuery->addOrderBy($columns);

return $activeQuery;
}

Expand Down
33 changes: 19 additions & 14 deletions tests/NestedSetsQueryBehaviorTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -75,20 +75,31 @@ public function testRootsMethodRequiresOrderByForCorrectTreeTraversal(): void
{
$this->createDatabase();

$rootD = new MultipleTree(['name' => 'Root D']);
$rootD->makeRoot();

$rootB = new MultipleTree(['name' => 'Root B']);
$rootB->makeRoot();

$rootA = new MultipleTree(['name' => 'Root A']);

$rootA->makeRoot();

$rootC = new MultipleTree(['name' => 'Root C']);

$rootC->makeRoot();

$rootB = new MultipleTree(['name' => 'Root B']);

$rootB->makeRoot();

$rootD = new MultipleTree(['name' => 'Root D']);

$rootD->makeRoot();
$command = $this->getDb()->createCommand();

$command->update('multiple_tree', ['tree' => 1], ['name' => 'Root A'])->execute();
$command->update('multiple_tree', ['tree' => 2], ['name' => 'Root B'])->execute();
$command->update('multiple_tree', ['tree' => 3], ['name' => 'Root C'])->execute();
$command->update('multiple_tree', ['tree' => 4], ['name' => 'Root D'])->execute();

$rootsList = MultipleTree::find()->roots()->all();
$expectedOrder = ['Root D', 'Root B', 'Root A', 'Root C'];

$expectedOrder = ['Root A', 'Root B', 'Root C', 'Root D'];

self::assertCount(
4,
Expand All @@ -102,17 +113,11 @@ public function testRootsMethodRequiresOrderByForCorrectTreeTraversal(): void
$root,
"Root at index {$index} should be an instance of \'MultipleTree\'.",
);
self::assertArrayHasKey(
$index,
$expectedOrder,
"Expected order array should have key at index {$index}.",
);

if (isset($expectedOrder[$index])) {
self::assertEquals(
$expectedOrder[$index],
$root->getAttribute('name'),
"Root at index {$index} should be {$expectedOrder[$index]} in correct \'id\' order.",
"Root at index {$index} should be {$expectedOrder[$index]} in correct \'tree\' order.",
);
}
}
Expand Down