-
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathNestedSetsQueryBehavior.php
More file actions
144 lines (127 loc) · 4.64 KB
/
NestedSetsQueryBehavior.php
File metadata and controls
144 lines (127 loc) · 4.64 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
<?php
declare(strict_types=1);
namespace yii2\extensions\nestedsets;
use LogicException;
use yii\base\Behavior;
use yii\db\{ActiveQuery, Expression};
/**
* Behavior for {@see ActiveQuery} to support nested sets tree queries.
*
* Provides query methods for retrieving root and leaf nodes in a nested sets tree structure using Yii
* {@see ActiveQuery}.
*
* This behavior is designed to be attached to an {@see ActiveQuery} instance for models implementing the nested sets
* pattern.
*
* It enables convenient retrieval of root nodes (nodes with left attribute equal to `1`) and leaf nodes (nodes where
* right = left + `1`), supporting optional tree attribute sorting for multi-tree scenarios.
*
* Key features.
* - Compatible with Yii {@see ActiveQuery} and {@see Expression} for advanced query building.
* - Query for leaf nodes (nodes without children).
* - Query for root nodes in a nested set.
* - Supports custom left, right, and tree attribute names as defined in the model.
*
* @phpstan-template T of ActiveQuery
*
* @phpstan-extends Behavior<T>
*
* @copyright Copyright (C) 2023 Terabytesoftw.
* @license https://opensource.org/license/bsd-3-clause BSD 3-Clause License.
*/
class NestedSetsQueryBehavior extends Behavior
{
/**
* Retrieves all leaf nodes (nodes without children) in the nested sets tree.
*
* Selects nodes where the right attribute equals the left attribute plus one, indicating that the node has no
* children.
*
* 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 terminal nodes in a hierarchical structure managed by the
* nested sets pattern.
*
* @return ActiveQuery Query instance with leaf node conditions applied.
*
* Usage example:
* ```php
* // Get all leaf nodes in the tree
* $leaves = $model::find()->leaves()->all();
* ```
*
* @phpstan-return T
*/
public function leaves(): ActiveQuery
{
$class = $this->getOwner()->modelClass;
$model = new $class();
$db = $model::getDb();
$columns = [$model->leftAttribute => SORT_ASC];
if ($model->treeAttribute !== false) {
$columns = [$model->treeAttribute => SORT_ASC] + $columns;
}
$this->getOwner()
->andWhere(
[$model->rightAttribute => new Expression(
$db->quoteColumnName($model->leftAttribute) . '+ 1',
)],
)
->addOrderBy($columns);
return $this->getOwner();
}
/**
* Retrieves all root nodes in the nested sets tree.
*
* Selects nodes where the left attribute equals `1`, indicating root nodes in the hierarchical structure.
*
* 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.
*
* @return ActiveQuery Query instance with root node conditions applied.
*
* Usage example:
* ```php
* // Get all root nodes in the tree
* $roots = $model::find()->roots()->all();
* ```
*
* @phpstan-return T
*/
public function roots(): ActiveQuery
{
$class = $this->getOwner()->modelClass;
$model = new $class();
$columns = [$model->leftAttribute => SORT_ASC];
if ($model->treeAttribute !== false) {
$columns = [$model->treeAttribute => SORT_ASC] + $columns;
}
$activeQuery = $this->getOwner()->andWhere([$model->leftAttribute => 1]);
$activeQuery->addOrderBy($columns);
return $activeQuery;
}
/**
* Returns the {@see ActiveQuery} instance that owns this behavior.
*
* Ensures that the behavior has a valid owner before performing any operations that require access to the query
* instance.
*
* This method is used internally by all operations that manipulate the nested set structure, providing type safety
* and a clear error if the behavior is not attached.
*
* @throws LogicException if the behavior is not attached to an owner instance.
*
* @return ActiveQuery Owner query instance to which this behavior is attached.
*
* @phpstan-return T
*/
private function getOwner(): ActiveQuery
{
if ($this->owner === null) {
throw new LogicException('The "owner" property must be set before using the behavior.');
}
return $this->owner;
}
}