NestedSet::listNodesBuildTreeWithChildren()   A
last analyzed

Complexity

Conditions 6
Paths 6

Size

Total Lines 25
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 14
CRAP Score 6

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 14
c 1
b 0
f 0
dl 0
loc 25
ccs 14
cts 14
cp 1
rs 9.2222
cc 6
nc 6
nop 2
crap 6
1
<?php
2
3
namespace kalanis\nested_tree;
4
5
use kalanis\nested_tree\Sources\SourceInterface;
6
7
/**
8
 * Nested Set class for build left, right, level data.
9
 *
10
 * @see http://mikehillyer.com/articles/managing-hierarchical-data-in-mysql/ Query references.
11
 */
12
class NestedSet
13
{
14 139
    public function __construct(
15
        protected readonly SourceInterface $source,
16
        protected readonly Support\Node $nodeBase = new Support\Node(),
17
        protected readonly Support\TableSettings $tableSettings = new Support\TableSettings(),
18
    ) {
19 139
    }
20
21
    /**
22
     * Add new record to structure
23
     * The position is set to end
24
     *
25
     * Note: Properties of tree will be skipped and filled later. For their change use different methods.
26
     *
27
     * @param Support\Node $node
28
     * @param Support\Options $options
29
     * @return Support\Node
30
     */
31 3
    public function add(Support\Node $node, Support\Options $options = new Support\Options()) : Support\Node
32
    {
33 3
        $node->position = $this->getNewPosition($node->parentId, $options->where);
34
35 3
        return $this->source->add($node, $options->where);
36
    }
37
38
    /**
39
     * Update current record in structure
40
     *
41
     * Note: Properties with Null value will be skipped and stay same in the storage.
42
     * Note: Properties of tree will be skipped too. For their change use different methods.
43
     *
44
     * @param Support\Node $node
45
     * @param Support\Options $options
46
     * @return bool
47
     */
48 5
    public function update(Support\Node $node, Support\Options $options = new Support\Options()) : bool
49
    {
50 5
        return $this->source->updateData($node, $options->where);
51
    }
52
53
    /**
54
     * Change parent node to different one; put the content on the last position
55
     * Parent id can be either some number for existing one or 0/null for root
56
     *
57
     * Return true for pass, false otherwise
58
     *
59
     * @param int<1, max> $nodeId
60
     * @param int<0, max>|null $newParentId
61
     * @param Support\Options $options
62
     * @return bool
63
     */
64 7
    public function changeParent(int $nodeId, ?int $newParentId, Support\Options $options = new Support\Options()) : bool
65
    {
66 7
        $currentNode = $this->getNodeById($nodeId);
67 7
        if (empty($currentNode)) {
68 1
            return false;
69
        }
70
71 7
        if (!empty($newParentId)) {
72 5
            $parentNode = $this->getNodeById($newParentId);
73 5
            if (empty($parentNode)) {
74 5
                return false;
75
            }
76 2
        } elseif (is_null($newParentId)) {
0 ignored issues
show
introduced by
The condition is_null($newParentId) is always true.
Loading history...
77 1
            $parentNode = null;
78
        } else {
79 1
            $parentNode = new Support\Node();
80
        }
81
82 6
        if (!$this->isNewParentOutsideCurrentNodeTree($nodeId, $newParentId, $options)) {
83 4
            return false;
84
        }
85 6
        $newPosition = $this->getNewPosition($parentNode, $options->where);
86
87 6
        return $this->source->updateNodeParent($currentNode, $parentNode, $newPosition, $options->where);
88
    }
89
90
    /**
91
     * Move node to new position
92
     *
93
     * @param int<1, max> $nodeId
94
     * @param int<0, max> $newPosition
95
     * @param Support\Options $options
96
     * @return bool
97
     */
98 35
    public function move(int $nodeId, int $newPosition, Support\Options $options = new Support\Options()) : bool
99
    {
100 35
        $currentNode = $this->getNodeById($nodeId, $options);
101 35
        if (empty($currentNode)) {
102 8
            return false;
103
        }
104
105 28
        $parentNode = $this->source->selectParent($currentNode, $options);
106
107
        // move it
108 28
        if ($this->source->makeHole($parentNode, $newPosition, $newPosition > $currentNode->position, $options->where)) {
109 27
            return $this->source->updateNodeParent($currentNode, $parentNode, $newPosition, $options->where);
110
        }
111
112 1
        return false;
113
    }
114
115
    /**
116
     * Delete the selected taxonomy ID and pull children's parent ID to the same as selected one.<br>
117
     * Example: selected taxonomy ID is 4, its parent ID is 2. This method will be pull all children that has parent ID = 4 to 2 and delete the taxonomy ID 4.<br>
118
     * Always run <code>$NestedSet->rebuild()</code> after insert, update, delete to rebuild the correctly level, left, right data.
119
     *
120
     * @param int<1, max> $nodeId The selected taxonomy ID.
121
     * @param Support\Options $options Where array structure will be like this.
122
     * @return bool Return true on success, false for otherwise.
123
     */
124 8
    public function deletePullUpChildren(int $nodeId, Support\Options $options = new Support\Options()) : bool
125
    {
126
        // get this taxonomy parent id
127 8
        $node = $this->getNodeById($nodeId);
128 8
        if (empty($node)) {
129 1
            return false;
130
        }
131 8
        $parentNode = $this->source->selectParent($node, $options);
132
        // update this children first level.
133 8
        $this->source->updateChildrenParent($node, $parentNode, $options->where);
134
135 8
        return $this->source->deleteSolo($node, $options->where);
136
    }
137
138
    /**
139
     * Delete the selected taxonomy ID with its ALL children.<br>
140
     * Always run <code>$NestedSet->rebuild()</code> after insert, update, delete to rebuild the correctly level, left, right data.
141
     *
142
     * The columns `left`, `right` must have been built before using this method, otherwise the result will be incorrect.
143
     *
144
     * @param int<1, max> $nodeId The taxonomy ID to delete.
145
     * @param Support\Options $options Where array structure will be like this.
146
     * @return int|null Return number on success, return null for otherwise.
147
     */
148 7
    public function deleteWithChildren(int $nodeId, Support\Options $options = new Support\Options()) : ?int
149
    {
150 7
        $options->currentId = $nodeId;
151 7
        $options->unlimited = true;
152 7
        $result = $this->getNodesWithChildren($options);
153 7
        $i_count = 0;
154
155 7
        if (!empty($result->items)) {
156 4
            foreach ($result->items as $row) {
157 4
                if ($this->source->deleteSolo($row, $options->where)) {
158 4
                    $i_count++;
159
                }
160
            }
161
        }
162
163 7
        if (0 >= $i_count) {
164 3
            return null;
165
        }
166
167 4
        return $i_count;
168
    }
169
170
    /**
171
     * Get new position for taxonomy in the selected parent.
172
     *
173
     * @param int<0, max>|Support\Node|null $parent The parent ID. If root, set this to 0 or null.
174
     * @param Support\Conditions|null $where Where array structure will be like this.
175
     * @return int<1, max> Return the new position in the same parent.<br>
176
     *              WARNING! If there are no results or the results according to the conditions cannot be found. It always returns 1.
177
     */
178 13
    public function getNewPosition(int|Support\Node|null $parent, ?Support\Conditions $where = null) : int
179
    {
180 13
        $parentNode = is_object($parent)
181 5
            ? $parent
182 13
            : (
183 8
                is_null($parent)
184 4
                    ? null
185 8
                    : (
186 6
                        0 === $parent
187 2
                            ? new Support\Node()
188 8
                            : $this->getNodeById($parent)
189 8
                    )
190 13
            );
191 13
        $lastPosition = $this->source->selectLastPosition($parentNode, $where);
192
193 13
        return null === $lastPosition ? 1 : $lastPosition + 1;
194
    }
195
196
    /**
197
     * Get taxonomy from selected item and fetch its ALL children.<br>
198
     * Example: There are taxonomy tree like this. Root 1 > 1.1 > 1.1.1, Root 2, Root 3 > 3.1, Root 3 > 3.2 > 3.2.1, Root 3 > 3.2 > 3.2.2, Root 3 > 3.3<br>
199
     * Assume that selected item is Root 3. So, the result will be Root 3 > 3.1, 3.2 > 3.2.1, 3.2.2, 3.3<br>
200
     *
201
     * Warning! Even this method has options for search, custom where conditions,
202
     * but it is recommended that you should set the option to select only specific item.<br>
203
     * This method is intended to show results from a single target.
204
     *
205
     * The columns `left`, `right` must have been built before using this method, otherwise the result will be incorrect.
206
     *
207
     * @param Support\Options $options Available options
208
     *
209
     * @return Support\Result Return object of taxonomy data
210
     */
211 13
    public function getNodesWithChildren(Support\Options $options = new Support\Options()) : Support\Result
212
    {
213
        // set unwanted options that is available in `listTaxonomy()` method to defaults
214 13
        $options->parentId = null;
215 13
        $options->filterIdBy = [];
216 13
        $options->noSortOrder = false;
217
        // set required option.
218 13
        $options->listFlattened = true;
219
220 13
        return $this->listNodes($options);
221
    }
222
223
    /**
224
     * Get simple taxonomy from all known; set things to make the query more limited
225
     *
226
     * @param Support\Options $options
227
     * @return Support\Node|null
228
     */
229 2
    public function getNode(Support\Options $options = new Support\Options()) : ?Support\Node
230
    {
231 2
        $options = clone $options;
232 2
        $options->unlimited = false;
233 2
        $options->offset = 0;
234 2
        $options->limit = 1;
235 2
        $options->noSortOrder = false;
236 2
        $nodes = $this->source->selectLimited($options);
237 2
        if (empty($nodes)) {
238 1
            return null;
239
        }
240
241 1
        return reset($nodes);
242
    }
243
244
    /**
245
     * Get simple taxonomy by its ID; set things to make the query more limited
246
     *
247
     * @param int $nodeId
248
     * @param Support\Options $options
249
     * @return Support\Node|null
250
     */
251 54
    public function getNodeById(int $nodeId, Support\Options $options = new Support\Options()) : ?Support\Node
252
    {
253 54
        $customOptions = clone $options;
254 54
        $customOptions->currentId = $nodeId;
255
256 54
        $nodes = $this->source->selectSimple($customOptions);
257 54
        if (empty($nodes)) {
258 13
            return null;
259
        }
260
261 47
        return reset($nodes);
262
    }
263
264
    /**
265
     * Get taxonomy from selected item and fetch its parent in a line until root item.<br>
266
     * Example: There are taxonomy tree like this. Root1 > 1.1 > 1.1.1 > 1.1.1.1<br>
267
     * Assume that you selected at 1.1.1. So, the result will be Root1 > 1.1 > 1.1.1<br>
268
     * But if you set 'skipCurrent' to true the result will be Root1 > 1.1
269
     *
270
     * Warning! Even this method has options for search, custom where conditions,
271
     * but it is recommended that you should set the option to select only specific item.<br>
272
     * This method is intended to show results from a single target.
273
     *
274
     * The columns `left`, `right` must have been built before using this method, otherwise the result will be incorrect.
275
     *
276
     * @see http://mikehillyer.com/articles/managing-hierarchical-data-in-mysql/ Original source.
277
     * @param Support\Options $options Available options
278
     * @return Support\Result Return array object of taxonomy data
279
     */
280 18
    public function getNodesWithParents(Support\Options $options = new Support\Options()) : Support\Result
281
    {
282 18
        $result = new Support\Result();
283 18
        $result->items = $this->source->selectWithParents($options);
284 18
        $result->count = count($result->items);
285
286 18
        return $result;
287
    }
288
289
    /**
290
     * Rebuild children into array.
291
     *
292
     * @internal This method was called from `getTreeWithChildren()`.
293
     * @param array<int<-1, max>, Support\Node> $array The array data that was get while running `getTreeWithChildren()`. This data contains 'children' object property but empty, it will be added here.
0 ignored issues
show
Documentation Bug introduced by
The doc comment array<int<-1, max>, Support\Node> at position 2 could not be parsed: Expected '>' at position 2, but found 'int'.
Loading history...
294
     * @return array<int<-1, max>, Support\Node> Return added correct id of the children to data.
0 ignored issues
show
Documentation Bug introduced by
The doc comment array<int<-1, max>, Support\Node> at position 2 could not be parsed: Expected '>' at position 2, but found 'int'.
Loading history...
295
     */
296 129
    protected function getTreeRebuildChildren(array $array) : array
297
    {
298 129
        foreach ($array as $id => $row) {
299
            if (
300 129
                !is_null($row->parentId)
301 129
                && !empty($array[$row->parentId])
302 129
                && ($row->parentId !== $row->id)
303
            ) {
304 129
                $array[$row->parentId]->childrenIds[$id] = $id;
305 129
                $array[$row->parentId]->childrenNodes[$id] = $row;
306
            } elseif (
307 129
                is_null($row->parentId)
308 129
                && $this->tableSettings->rootIsNull
309 129
                && !empty($row->id)
310 129
                && ($this->tableSettings->virtualRootId !== $row->id)
311
            ) {
312 30
                $array[$this->tableSettings->virtualRootId]->childrenIds[$id] = $id;
313 30
                $array[$this->tableSettings->virtualRootId]->childrenNodes[$id] = $row;
314
            }
315
        }
316
317 129
        return $array;
318
    }
319
320
    /**
321
     * Get the data nest tree with children.<br>
322
     * Its result will be look like this...<pre>
323
     * Array(
324
     *     [0] => Support\Node Object
325
     *         (
326
     *             [id] => 0
327
     *             [children] => Array
328
     *                 (
329
     *                     [1] => 1
330
     *                     [2] => 2
331
     *                     [3] => 3
332
     *                 )
333
     *         )
334
     *     [1] => Support\Node Object
335
     *         (
336
     *             [id] => 1
337
     *             [parent_id] => 0
338
     *             [level] => 1
339
     *             [children] => Array
340
     *                 (
341
     *                 )
342
     *         )
343
     *     [2] => Support\Node Object
344
     *         (
345
     *             [id] => 2
346
     *             [parent_id] => 0
347
     *             [level] => 1
348
     *             [children] => Array
349
     *                 (
350
     *                     [4] => 4
351
     *                     [5] => 5
352
     *                 )
353
     *         )
354
     *     [3] => Support\Node Object
355
     *         (
356
     *             [id] => 3
357
     *             [parent_id] => 0
358
     *             [level] => 1
359
     *             [children] => Array
360
     *                 (
361
     *                 )
362
     *         )
363
     *     [4] => Support\Node Object
364
     *         (
365
     *             [id] => 4
366
     *             [parent_id] => 2
367
     *             [level] => 2
368
     *             [children] => Array
369
     *                 (
370
     *                 )
371
     *         )
372
     *     [5] => Support\Node Object
373
     *         (
374
     *             [id] => 5
375
     *             [parent_id] => 2
376
     *             [level] => 2
377
     *             [children] => Array
378
     *                 (
379
     *                 )
380
     *         )
381
     * )</pre>
382
     *
383
     * Usually, this method is for get taxonomy tree data in the array format that suit for loop/nest loop verify level.
384
     *
385
     * @since 1.0
386
     * @internal This method was called from `rebuild()`.
387
     * @param Support\Options $options Where array structure will be like this.
388
     * @return array<int<-1, max>, Support\Node> Return formatted array structure as seen in example of docblock.
0 ignored issues
show
Documentation Bug introduced by
The doc comment array<int<-1, max>, Support\Node> at position 2 could not be parsed: Expected '>' at position 2, but found 'int'.
Loading history...
389
     */
390 126
    protected function getTreeWithChildren(Support\Options $options = new Support\Options()) : array
391
    {
392
        // create a root node to hold child data about first level items
393 126
        $result = $this->source->selectSimple($options);
394
        // map ids to keys
395 126
        $result = array_combine(array_map(fn (Support\Node $node) : int => $node->id, $result), $result);
396
        // fill root one
397 126
        if (empty($result[$this->tableSettings->virtualRootId])) {
398
            // !!! beware that this creates a virtual node!
399 126
            $virtualRoot = clone $this->nodeBase; // hack for root node
400 126
            $virtualRoot->id = $this->tableSettings->virtualRootId; // must be outside the db values
401 126
            $virtualRoot->left = -1; // must be outside the db values
402 126
            $virtualRoot->level = $this->tableSettings->rootLevel - 1; // must be lower than the actual root level
403 126
            $result[$this->tableSettings->virtualRootId] = $virtualRoot;
404
        }
405
406
        // now process the array and build the child data
407 126
        return $this->getTreeRebuildChildren($result);
408
    }
409
410
    /**
411
     * Detect that is this taxonomy's parent setting to be under this taxonomy's children or not.<br>
412
     * For example: Root 1 > 1.1 > 1.1.1 > 1.1.1.1 > 1.1.1.1.1<br>
413
     * Assume that you are editing 1.1.1 and its parent is 1.1. Now you change its parent to 1.1.1.1.1 which is under its children.<br>
414
     * The parent of 1.1.1 must be root, Root 1, 1.1 and never go under that.
415
     *
416
     * @param int<1, max> $currentNodeId The taxonomy ID that is changing the parent.
417
     * @param int<0, max>|null $newParentId The selected parent ID to check.
418
     * @param Support\Options $options Where array structure will be like this.
419
     * @return bool Return `false` if its parent is under its children (INCORRECT changes).<br>
420
     *              Return `false` if search result was not found (INCORRECT changes).<br>
421
     *              Return `true` if its parent is not under its children (CORRECT changes).
422
     */
423 10
    public function isNewParentOutsideCurrentNodeTree(int $currentNodeId, ?int $newParentId, Support\Options $options = new Support\Options()) : bool
424
    {
425 10
        if (empty($newParentId)) {
426
            // if parent is root, always return true because that is always correct!
427 3
            return true;
428
        }
429
430
        // check for selected parent that must not under this taxonomy.
431 8
        $options->currentId = $newParentId;
432 8
        $nodesWithParents = $this->getNodesWithParents($options);
433
434 8
        if (!empty($nodesWithParents->items)) {
435 8
            foreach ($nodesWithParents->items as $row) {
436 8
                if ($row->parentId === $currentNodeId) {
437 8
                    return false;
438
                }
439
            }
440
441 8
            return true;
442
        }
443
444 2
        return false;
445
    }
446
447
    /**
448
     * List taxonomy.
449
     *
450
     * The columns `left`, `right` must have been built before using this method, otherwise the result will be incorrect.
451
     *
452
     * @param Support\Options $options Available options
453
     */
454 83
    public function listNodes(Support\Options $options = new Support\Options()) : Support\Result
455
    {
456 83
        $output = new Support\Result();
457 83
        $output->count = $this->source->selectCount($options);
458 83
        $output->items = $this->source->selectLimited($options);
459
460 83
        if (!$options->listFlattened) {
461 26
            $output = $this->listNodesBuildTreeWithChildren($output, $options);
462
        }
463
464 83
        return $output;
465
    }
466
467
    /**
468
     * Build tree data with children.
469
     *
470
     * @internal This method was called from `listTaxonomy()`.
471
     * @param Support\Result $result The array item get from fetchAll() method using the PDO.
472
     * @param Support\Options $options Available options
473
     * @return Support\Result Return array data of formatted values.
474
     */
475 26
    protected function listNodesBuildTreeWithChildren(Support\Result $result, Support\Options $options) : Support\Result
476
    {
477 26
        $items = [];
478 26
        foreach ($result->items as &$item) {
479 25
            $items[$item->parentId][] = $item;
480
        }
481
482 26
        if (empty($options->filterIdBy)) {
483
            // without taxonomy_id_in option exists, this result can format to be hierarchical.
484 22
            foreach ($result->items as $row) {
485 21
                if (isset($items[$row->id])) {
486 19
                    $row->childrenNodes = $items[$row->id];
487 19
                    $row->childrenIds = array_map(fn (Support\Node $node) => $node->id, $row->childrenNodes);
488
                }
489
            }
490
491 22
            $partItems = ($items[0] ?? array_shift($items)); // this is important ([0]) for prevent duplicate items
492 22
            if (empty($partItems)) {
493 1
                return new Support\Result();
494
            } else {
495 21
                $result->items = $partItems;
496
            }
497
        }
498
499 25
        return $result;
500
    }
501
502
    /**
503
     * List taxonomy as flatten not tree.<br>
504
     * All parameters or arguments are same as `listTaxonomy()` method.
505
     *
506
     * @param Support\Options $options Available options
507
     */
508 47
    public function listNodesFlatten(Support\Options $options = new Support\Options()) : Support\Result
509
    {
510 47
        $options->listFlattened = true;
511
512 47
        return $this->listNodes($options);
513
    }
514
515
    /**
516
     * Rebuilds the tree data and save it to the database.<br>
517
     * This will rebuild the level, left, right values.
518
     *
519
     * The columns `left`, `right` must have been built before using this method, otherwise the result will be incorrect.
520
     *
521
     * @param Support\Options $options Where array structure will be like this.
522
     */
523 125
    public function rebuild(Support\Options $options = new Support\Options()) : void
524
    {
525
        // get taxonomy tree data in the array format that suit for loop/nest loop verify level.
526 125
        $data = $this->getTreeWithChildren($options);
527
528
        // because it's possible that data reference themselves I need to remove them from the circle
529 125
        $this->clearChildrenForSelfReferences($data);
530
531 125
        $n = $data[$this->tableSettings->virtualRootId]->left + 1; // need a variable to hold the running n tally
532 125
        $p = 0; // need a variable to hold the running position tally
533
534
        // rebuild positions
535 125
        $this->rebuildGeneratePositionData($data, $this->tableSettings->virtualRootId, $p);
536
537
        // verify the level data. this method will alter the $data value because it will be called as reference.
538
        // so, it doesn't need to use `$data = $this->rebuildGenerateTreeData()`;
539 125
        $this->rebuildGenerateTreeData($data, $this->tableSettings->virtualRootId, $this->tableSettings->rootLevel, $n);
540
541 125
        foreach ($data as $row) {
542 125
            if ($this->tableSettings->rootIsNull && $this->tableSettings->virtualRootId === $row->id) {
543
                // this shall skip the virtual root
544
                // normal one will need to be updated too
545 30
                continue;
546
            }
547
548 125
            $this->source->updateLeftRightPos($row, $options->where);
549
        }
550
    }
551
552
    /**
553
     * Rebuild taxonomy level, left, right for tree data.<br>
554
     * This method will alter the $array value. It will be set level, left, right value.
555
     *
556
     * This method modify variables via argument reference without return anything.
557
     *
558
     * @internal This method was called from `rebuild()`.
559
     * @param array<int<-1, max>, Support\Node> $array The data array, will be call as reference and modify its value.
0 ignored issues
show
Documentation Bug introduced by
The doc comment array<int<-1, max>, Support\Node> at position 2 could not be parsed: Expected '>' at position 2, but found 'int'.
Loading history...
560
     * @param int<-1, max> $id The ID of taxonomy.
561
     * @param int<-1, max> $level The level of taxonomy.
562
     * @param int<0, max> $n The tally or count number, will be call as reference and modify its value.
563
     */
564 126
    protected function rebuildGenerateTreeData(array &$array, int $id, int $level, int &$n) : void
565
    {
566 126
        $array[$id]->level = $level;
567 126
        $array[$id]->left = $n++;
568
569
        // loop over the node's children and process their data
570
        // before assigning the right value
571 126
        foreach ($array[$id]->childrenIds as $childNodeId) {
572 126
            $this->rebuildGenerateTreeData($array, $childNodeId, $level + 1, $n);
573
        }
574
575 126
        $array[$id]->right = $n++;
576
    }
577
578
    /**
579
     * Rebuild taxonomy positions for tree data.<br>
580
     * This method will alter the $array value. It will set position value.
581
     *
582
     * This method modify variables via argument reference without return anything.
583
     *
584
     * @internal This method was called from `rebuild()`.
585
     * @param array<int<-1, max>, Support\Node> $array The data array, will be call as reference and modify its value.
0 ignored issues
show
Documentation Bug introduced by
The doc comment array<int<-1, max>, Support\Node> at position 2 could not be parsed: Expected '>' at position 2, but found 'int'.
Loading history...
586
     * @param int<-1, max> $id The ID of taxonomy.
587
     * @param int<0, max> $n The position number, will be call as reference and modify its value.
588
     */
589 126
    protected function rebuildGeneratePositionData(array &$array, int $id, int &$n) : void
590
    {
591 126
        $array[$id]->position = ++$n;
592
593
        // loop over the node's children and process their data
594
        // before assigning the right value
595 126
        $p = 0;
596 126
        foreach ($array[$id]->childrenIds as $childNodeId) {
597 126
            $this->rebuildGeneratePositionData($array, $childNodeId, $p);
598
        }
599
600 126
        usort($array[$id]->childrenIds, function (int $a, int $b) use ($array) {
601 126
            return $array[$a]->position <=> $array[$b]->position;
602 126
        });
603
    }
604
605
    /**
606
     * Clear data of references to Self.<br>
607
     * This method will alter the $array value. It will remove the ids of entries which references the entry itself
608
     *
609
     * This method modify variables via argument reference without return anything.
610
     *
611
     * @internal This method was called from `rebuild()`.
612
     * @param array<int<-1, max>, Support\Node> $array The data array, will be call as reference and modify its value.
0 ignored issues
show
Documentation Bug introduced by
The doc comment array<int<-1, max>, Support\Node> at position 2 could not be parsed: Expected '>' at position 2, but found 'int'.
Loading history...
613
     */
614 125
    protected function clearChildrenForSelfReferences(array &$array) : void
615
    {
616
        // loop over the node's children and process their data
617 125
        foreach ($array as &$node) {
618 125
            $node->childrenIds = array_filter(
619 125
                $node->childrenIds,
620 125
                fn (int $id) : bool => $node->id !== $id
621 125
            );
622 125
            $node->childrenNodes = array_filter(
623 125
                $node->childrenNodes,
624 125
                fn (Support\Node $child) : bool => in_array($child->id, $node->childrenIds)
625 125
            );
626
        }
627
    }
628
}
629