12
12
/**
13
13
* Nested set behavior for managing hierarchical data in {@see ActiveRecord} models.
14
14
*
15
- * Provides a set of methods and properties to implement the nested sets pattern in Yii {@see ActiveRecord} models,
16
- * enabling efficient management of hierarchical data structures such as trees and categories.
15
+ * Provides a set of methods and properties to implement the nested sets pattern in Yii Active Record model, enabling
16
+ * efficient management of hierarchical data structures such as trees and categories.
17
17
*
18
18
* This behavior allows nodes to be inserted, moved, or deleted within the tree, and supports querying for parents,
19
19
* children, leaves, and siblings.
20
20
*
21
21
* The behavior manages the left, right, and depth attributes of each node, and can optionally support multiple trees
22
22
* using a tree attribute.
23
23
*
24
- * It integrates with a Yii event system and can be attached to any {@see ActiveRecord} model.
24
+ * It integrates with a Yii Event system and can be attached to any {@see ActiveRecord} model.
25
25
*
26
26
* Key features.
27
27
* - Compatible with Yii {@see ActiveRecord} and event system.
@@ -515,8 +515,6 @@ public function children(int|null $depth = null): ActiveQuery
515
515
*
516
516
* If the deletion fails, the transaction is rolled back to maintain data integrity.
517
517
*
518
- * @throws Exception if an unexpected error occurs during execution.
519
- *
520
518
* @return bool|int Number of rows deleted, or false if the deletion is unsuccessful.
521
519
*
522
520
* Usage example:
@@ -809,7 +807,6 @@ public function leaves(): ActiveQuery
809
807
*
810
808
* Sets the internal operation state to {@see self::OPERATION_MAKE_ROOT} and triggers the save process on the owner
811
809
* model.
812
- *
813
810
* - If the attached {@see ActiveRecord} is a new record, this method creates it as the root node of a new tree,
814
811
* setting `left=1`, `right=2`, and `depth=0`.
815
812
* - If the record already exists, it moves the node to become the root node, updating the nested set structure
@@ -1094,7 +1091,7 @@ protected function moveNode(NodeContext $context): void
1094
1091
1095
1092
if ($ this ->treeAttribute === false || $ currentTreeValue === $ targetTreeValue ) {
1096
1093
$ this ->executeSameTreeMove ($ context , $ currentTreeValue );
1097
- } elseif ( $ this -> treeAttribute !== false ) {
1094
+ } else {
1098
1095
$ this ->executeCrossTreeMove ($ context , $ this ->treeAttribute , $ currentTreeValue , $ targetTreeValue );
1099
1096
}
1100
1097
}
@@ -1246,6 +1243,25 @@ private function deleteWithChildrenInternal(): bool|int
1246
1243
return $ result ;
1247
1244
}
1248
1245
1246
+ /**
1247
+ * Executes a cross-tree move operation for the current node and its descendants.
1248
+ *
1249
+ * Handles the relocation of a subtree from one tree to another in a multi-tree nested set structure, updating left,
1250
+ * right, and depth attributes, and shifting affected nodes in the target tree to maintain integrity.
1251
+ *
1252
+ * This method is called internally when a node is moved across trees, ensuring that all boundaries and depth levels
1253
+ * are recalculated and the subtree is correctly positioned in the new tree context.
1254
+ *
1255
+ * The operation performs the following steps.
1256
+ * - Closes the gap left in the source tree by shifting left and right attributes of remaining nodes.
1257
+ * - Moves the subtree to the target tree, updating tree, left, right, and depth attributes accordingly.
1258
+ * - Shifts left and right attribute values in the target tree to make space for the incoming subtree.
1259
+ *
1260
+ * @param NodeContext $context Immutable context containing all movement data for the operation.
1261
+ * @param string $treeAttribute Name of the tree attribute used for multi-tree support.
1262
+ * @param mixed $currentTreeValue Value of the tree attribute for the current (source) tree.
1263
+ * @param mixed $targetTreeValue Value of the tree attribute for the target tree.
1264
+ */
1249
1265
private function executeCrossTreeMove (
1250
1266
NodeContext $ context ,
1251
1267
string $ treeAttribute ,
@@ -1284,6 +1300,27 @@ private function executeCrossTreeMove(
1284
1300
}
1285
1301
1286
1302
/**
1303
+ * Executes a nested set operation on the current node with the specified target and operation type.
1304
+ *
1305
+ * Sets the internal operation state and target node reference, then save the owner model to perform the requested
1306
+ * structural change in the tree.
1307
+ *
1308
+ * After saving, it refreshes the target or owner as needed to ensure updated attribute values for append and make
1309
+ * root operations.
1310
+ *
1311
+ * This method is used internally by public API methods such as {@see appendTo()}, {@see insertAfter()},
1312
+ * {@see insertBefore()}, {@see prependTo()}, and {@see makeRoot()} to centralize the execution logic for all
1313
+ * supported nested set operations.
1314
+ *
1315
+ * @param ActiveRecord|null $targetNode Target node for the operation, or `null` if not applicable.
1316
+ * @param string $operation Operation type to perform (see OPERATION_* constants).
1317
+ * @param bool $runValidation Whether to perform validation before saving the record.
1318
+ * @param array<string, mixed>|null $attributes List of attributes to save, or `null` for all attributes.
1319
+ *
1320
+ * @throws Exception if an unexpected error occurs during execution.
1321
+ *
1322
+ * @return bool Whether the operation was successful and the node was saved.
1323
+ *
1287
1324
* @phpstan-param array<string, mixed>|null $attributes
1288
1325
*/
1289
1326
private function executeOperation (
@@ -1308,6 +1345,25 @@ private function executeOperation(
1308
1345
return $ result ;
1309
1346
}
1310
1347
1348
+ /**
1349
+ * Moves the current node and its descendants to a new position within the same tree structure.
1350
+ *
1351
+ * Updates the left, right, and depth attributes for the node and all its descendants when moving a subtree to a
1352
+ * different position within the same tree, ensuring the nested set structure remains consistent.
1353
+ *
1354
+ * The method performs the following operations.
1355
+ * - Adjusts the left and right boundaries of the subtree if it is moved forward in the tree.
1356
+ * - Closes the gap left by the moved subtree by shifting left and right attributes of remaining nodes.
1357
+ * - Shifts left and right attribute values to make space for the moved subtree.
1358
+ * - Updates left and right attributes for all nodes in the subtree to reflect the new position.
1359
+ * - Updates the depth attribute for all nodes in the subtree based on the new target depth.
1360
+ *
1361
+ * This operation is essential for maintaining the integrity of the nested set hierarchy during node reordering
1362
+ * and is used internally by movement operations that do not cross tree boundaries.
1363
+ *
1364
+ * @param NodeContext $context Immutable context containing all movement data for the operation.
1365
+ * @param mixed $currentTreeValue Value of the tree attribute for the current tree.
1366
+ */
1311
1367
private function executeSameTreeMove (NodeContext $ context , mixed $ currentTreeValue ): void
1312
1368
{
1313
1369
$ subtreeSize = $ this ->getRightValue () - $ this ->getLeftValue () + 1 ;
@@ -1381,18 +1437,38 @@ private function getDb(): Connection
1381
1437
return $ this ->db ??= $ this ->getOwner ()::getDb ();
1382
1438
}
1383
1439
1440
+ /**
1441
+ * Retrieves and caches the depth value of the current node.
1442
+ *
1443
+ * The value is cached on first access to avoid redundant lookups during nested set operations.
1444
+ *
1445
+ * This method is used internally by movement and update operations to determine the current node depth within the
1446
+ * tree structure, ensuring correct calculation of depth offsets and validation of node positions.
1447
+ *
1448
+ * @return int Depth value of the current node as stored in the owner model.
1449
+ */
1384
1450
private function getDepthValue (): int
1385
1451
{
1386
1452
return $ this ->depthValue ??= $ this ->getOwner ()->getAttribute ($ this ->depthAttribute );
1387
1453
}
1388
1454
1455
+ /**
1456
+ * Retrieves and caches the left boundary value of the current node.
1457
+ *
1458
+ * The value is cached on first access to avoid redundant lookups during nested set operations.
1459
+ *
1460
+ * This method is used internally by movement and update operations to determine the current node left boundary
1461
+ * within the tree structure, ensuring correct calculation of node positions and validation of node movements.
1462
+ *
1463
+ * @return int Left boundary value of the current node as stored in the owner model.
1464
+ */
1389
1465
private function getLeftValue (): int
1390
1466
{
1391
1467
return $ this ->leftValue ??= $ this ->getOwner ()->getAttribute ($ this ->leftAttribute );
1392
1468
}
1393
1469
1394
1470
/**
1395
- * Returns the {@see ActiveRecord} instance to which this behavior is currently attached.
1471
+ * Retrieves the owner model instance to which this behavior is attached.
1396
1472
*
1397
1473
* Ensures that the behavior has a valid owner before performing any operations that require access to the model
1398
1474
* instance.
@@ -1415,6 +1491,16 @@ private function getOwner(): ActiveRecord
1415
1491
return $ this ->owner ;
1416
1492
}
1417
1493
1494
+ /**
1495
+ * Retrieves and caches the right boundary value of the current node.
1496
+ *
1497
+ * The value is cached on first access to avoid redundant lookups during nested set operations.
1498
+ *
1499
+ * This method is used internally by movement and update operations to determine the current node right boundary
1500
+ * within the tree structure, ensuring correct calculation of node positions and validation of node movements.
1501
+ *
1502
+ * @return int Right boundary value of the current node as stored in the owner model.
1503
+ */
1418
1504
private function getRightValue (): int
1419
1505
{
1420
1506
return $ this ->rightValue ??= $ this ->getOwner ()->getAttribute ($ this ->rightAttribute );
0 commit comments