NodeMoveAction::applyRootCondition()   A
last analyzed

Complexity

Conditions 2
Paths 2

Size

Total Lines 6
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 6
rs 9.4285
c 0
b 0
f 0
cc 2
eloc 3
nc 2
nop 1
1
<?php
2
3
namespace devgroup\JsTreeWidget\actions\nestedset;
4
5
use devgroup\JsTreeWidget\widgets\TreeWidget;
6
use yii\base\Action;
7
use Yii;
8
use yii\base\InvalidConfigException;
9
use yii\db\ActiveRecord;
10
use yii\db\Expression;
11
use yii\web\Response;
12
13
/**
14
 * Class NodeMoveAction
15
 *
16
 * @package devgroup\JsTreeWidget\actions\nestedset
17
 */
18
class NodeMoveAction extends Action
19
{
20
    /** @var  ActiveRecord */
21
    public $className;
22
    /** @var string set root column name for multi root tree */
23
    public $rootAttribute = false;
24
    /** @var string */
25
    public $leftAttribute = 'lft';
26
    /** @var string */
27
    public $rightAttribute = 'rgt';
28
    /** @var string */
29
    public $depthAttribute = 'depth';
30
    /** @var int */
31
    public $depthLimit = false;
32
33
    /** @var  ActiveRecord */
34
    protected $node;
35
    /** @var  ActiveRecord */
36
    protected $parent;
37
    /** @var  string */
38
    protected $tableName;
39
40
    /**
41
     * @inheritdoc
42
     */
43 View Code Duplication
    public function init()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
44
    {
45
        if (true === empty($this->className) || false === is_subclass_of($this->className, ActiveRecord::class)) {
0 ignored issues
show
Bug introduced by
Due to PHP Bug #53727, is_subclass_of might return inconsistent results on some PHP versions if \yii\db\ActiveRecord::class can be an interface. If so, you could instead use ReflectionClass::implementsInterface.
Loading history...
46
            throw new InvalidConfigException('"className" param must be set and must be child of ActiveRecord');
47
        }
48
        /** @var ActiveRecord $class */
49
        $class = $this->className;
50
        $this->tableName = $class::tableName();
51
        $scheme = Yii::$app->getDb()->getTableSchema($this->tableName);
52
        $columns = $scheme->columns;
53
        if (false !== $this->rootAttribute && false === isset($columns[$this->rootAttribute])) {
54
            throw new InvalidConfigException("Column '{$this->rootAttribute}' not found in the '{$this->tableName}' table");
55
        }
56
        if (false === isset(
57
                $columns[$this->leftAttribute],
58
                $columns[$this->rightAttribute],
59
                $columns[$this->depthAttribute]
60
            )
61
        ) {
62
            throw new InvalidConfigException(
63
                "Some of the '{$this->leftAttribute}', '{$this->rightAttribute}', '{$this->depthAttribute}', "
64
                . "not found in the '{$this->tableName}' columns list"
65
            );
66
        }
67
        TreeWidget::registerTranslations();
68
        parent::init();
69
    }
70
71
    /**
72
     * @inheritdoc
73
     */
74
    public function run()
75
    {
76
        Yii::$app->response->format = Response::FORMAT_JSON;
77
        $newParentId = Yii::$app->request->post('parent');
78
        $oldParentId = Yii::$app->request->post('old_parent');
79
        $position = Yii::$app->request->post('position');
80
        $oldPosition = Yii::$app->request->post('old_position');
81
        $nodeId = Yii::$app->request->post('node_id');
82
        $siblings = Yii::$app->request->post('siblings', []);
83
        $class = $this->className;
84
        if ((int)$newParentId == 0) {
85
            return ['error' => Yii::t('jstw', 'Can not move node as root!')];
86
        }
87
        if ((null === $node = $class::findOne($nodeId)) || (null === $parent = $class::findOne($newParentId))) {
88
            return ['error' => Yii::t('jstw', 'Invalid node id or parent id received!')];
89
        }
90
        if ($this->depthLimit) {
91
            $nodeMaxDepth = $node->children()->select(new Expression('MAX(' . $this->depthAttribute . ')'))->scalar();
0 ignored issues
show
Bug introduced by
The method children cannot be called on $node (of type array|boolean).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
92
            $nodeMaxDepth = $nodeMaxDepth ? $nodeMaxDepth : $node->{$this->depthAttribute};
93
            $nodeResultDepth = $parent->{$this->depthAttribute} + ($nodeMaxDepth - $node->{$this->depthAttribute} + 1);
94
            if ($nodeResultDepth >= $this->depthLimit) {
95
                return ['error' => Yii::t('jstw', 'Can not move node because max depth ({depthLimit}) is exceeded!', ['depthLimit' => $this->depthLimit])];
96
            }
97
        }
98
        $this->node = $node;
0 ignored issues
show
Documentation Bug introduced by
It seems like $node of type array or boolean is incompatible with the declared type object<yii\db\ActiveRecord> of property $node.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
99
        $this->parent = $parent;
0 ignored issues
show
Documentation Bug introduced by
It seems like $parent of type array or boolean is incompatible with the declared type object<yii\db\ActiveRecord> of property $parent.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
100
        if (false !== $this->rootAttribute && ($node->{$this->rootAttribute} != $parent->{$this->rootAttribute})) {
101
            return $this->moveMultiRoot($position, $siblings, $oldParentId);
102
        }
103
        if ($newParentId == $oldParentId) {
104
            return $this->reorder($oldPosition, $position, $siblings);
105
        } else {
106
            return $this->move($position, $siblings, $oldParentId);
107
        }
108
    }
109
110
    /**
111
     * Moves node inside one parent inside one root
112
     *
113
     * @param null $oldPosition
114
     * @param null $position
115
     * @param array $siblings
116
     * @return array|bool
0 ignored issues
show
Documentation introduced by
Consider making the return type a bit more specific; maybe use array<string,string>|boolean.

This check looks for the generic type array as a return type and suggests a more specific type. This type is inferred from the actual code.

Loading history...
117
     * @throws \yii\db\Exception
118
     */
119
    protected function reorder($oldPosition = null, $position = null, $siblings = [])
120
    {
121
        if (null === $oldPosition || null === $position || true === empty($siblings)) {
122
            return ['error' => Yii::t('jstw', 'Invalid data provided!')];
123
        }
124
        $nodeId = $siblings[$position];
125
        $class = $this->className;
126
        if ($oldPosition > $position) {
127
            //change next
128
            $nodeOperator = '-';
129
            $siblingsOperator = '+';
130
            $workWith = array_slice($siblings, $position, $oldPosition - $position + 1);
131
        } else if ($oldPosition < $position) {
132
            //change previous
133
            $nodeOperator = '+';
134
            $siblingsOperator = '-';
135
            $workWith = array_slice($siblings, $oldPosition, $position - $oldPosition + 1);
136
        } else {
137
            return true;
138
        }
139
        if (true === empty($workWith)) {
140
            return ['error' => Yii::t('jstw', 'Invalid data provided!')];
141
        }
142
        $lr = $workWithLr = $this->getLr($workWith);
143
        if (true === empty($lr)) {
144
            return ['error' => Yii::t('jstw', 'Invalid data provided!')];
145
        }
146
        unset($workWithLr[$nodeId]);
147
        $lft = array_column($workWithLr, $this->leftAttribute);
148
        $lft = min($lft);
149
        $rgt = array_column($workWithLr, $this->rightAttribute);
150
        $rgt = max($rgt);
151
        $nodeCondition = [
152
            'and',
153
            ['>=', $this->leftAttribute, $lft],
154
            ['<=', $this->rightAttribute, $rgt]
155
        ];
156
        $this->applyRootCondition($nodeCondition);
157
        $nodeDelta = $this->getCount($nodeCondition);
158
        $nodeDelta *= 2;
159
        $siblingsCondition = [
160
            'and',
161
            ['>=', $this->leftAttribute, $lr[$nodeId][$this->leftAttribute]],
162
            ['<=', $this->rightAttribute, $lr[$nodeId][$this->rightAttribute]]
163
        ];
164
        $this->applyRootCondition($siblingsCondition);
165
        $nodeChildren = $this->getChildIds($siblingsCondition);
166
        $siblingsDelta = count($nodeChildren) * 2;
167
        $db = Yii::$app->getDb();
168
        $transaction = $db->beginTransaction();
169
        try {
170
            //updating necessary node siblings
171
            $db->createCommand()->update(
172
                $class::tableName(),
173
                [
174
                    $this->leftAttribute => new Expression($this->leftAttribute . sprintf('%s%d', $siblingsOperator, $siblingsDelta)),
175
                    $this->rightAttribute => new Expression($this->rightAttribute . sprintf('%s%d', $siblingsOperator, $siblingsDelta)),
176
                ],
177
                $nodeCondition
178
            )->execute();
179
            //updating node
180
            $db->createCommand()->update(
181
                $class::tableName(),
182
                [
183
                    $this->leftAttribute => new Expression($this->leftAttribute . sprintf('%s%d', $nodeOperator, $nodeDelta)),
184
                    $this->rightAttribute => new Expression($this->rightAttribute . sprintf('%s%d', $nodeOperator, $nodeDelta)),
185
                ],
186
                ['id' => $nodeChildren]
187
            )->execute();
188
            $transaction->commit();
189
        } catch (\Exception $e) {
190
            $transaction->rollBack();
191
            return ['error' => $e->getMessage()];
192
        }
193
        return true;
194
    }
195
196
    /**
197
     * Moves node inside one root
198
     *
199
     * @param null $position
200
     * @param array $siblings
201
     * @param string | integer $oldParentId
202
     * @return array|bool
0 ignored issues
show
Documentation introduced by
Consider making the return type a bit more specific; maybe use array<string,string>|boolean.

This check looks for the generic type array as a return type and suggests a more specific type. This type is inferred from the actual code.

Loading history...
203
     * @throws \yii\db\Exception
204
     */
205
    protected function move($position = null, $siblings = [], $oldParentId)
0 ignored issues
show
Coding Style introduced by
Parameters which have default values should be placed at the end.

If you place a parameter with a default value before a parameter with a default value, the default value of the first parameter will never be used as it will always need to be passed anyway:

// $a must always be passed; it's default value is never used.
function someFunction($a = 5, $b) { }
Loading history...
206
    {
207
        $class = $this->className;
208 View Code Duplication
        if (null === $oldParent = $class::findOne($oldParentId)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
209
            return ['error' => Yii::t('jstw', "Old parent with id '{id}' not found!", ['id' => $oldParentId])];
210
        }
211
        $nodeCountCondition = [
212
            'and',
213
            ['>=', $this->leftAttribute, $this->node{$this->leftAttribute}],
214
            ['<=', $this->rightAttribute, $this->node{$this->rightAttribute}]
215
        ];
216
        $this->applyRootCondition($nodeCountCondition);
217
        $nodeChildren = $this->getChildIds($nodeCountCondition);
218
        $siblingsDelta = count($nodeChildren) * 2;
219 View Code Duplication
        if ($position == 0) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
220
            $compareRight = $this->parent->{$this->leftAttribute} + 1;
221
        } else {
222
            if (false === isset($siblings[$position - 1])) {
223
                return ['error' => Yii::t('jstw', 'New previous sibling does not exist')];
224
            }
225
            $newPrevSiblingId = $siblings[$position - 1];
226
            $newPrevSiblingData = $this->getLr($newPrevSiblingId);
227
            $compareRight = $newPrevSiblingData[$newPrevSiblingId][$this->rightAttribute];
228
        }
229
        if ($this->node->{$this->leftAttribute} > $compareRight) {
230
            //move node up
231
            if ($position == 0) {
232
                $leftFrom = $compareRight;
233
            } else {
234
                $leftFrom = $compareRight + 1;
235
            }
236
            $rightTo = $this->node->{$this->leftAttribute};
237
            $nodeDelta = $this->node->{$this->leftAttribute} - $leftFrom;
238
            $nodeOperator = '-';
239
            $parentOperator = $siblingsOperator = '+';
240
            $newParentUpdateField = $this->rightAttribute;
241
            $oldParentUpdateField = $this->leftAttribute;
242
        } else if ($this->node->{$this->leftAttribute} < $compareRight) {
243
            //move node down
244
            $leftFrom = $this->node->{$this->rightAttribute};
245
            if ($position == 0) {
246
                $rightTo = $compareRight - 1;
247
            } else {
248
                $rightTo = $compareRight;
249
            }
250
            $nodeOperator = '+';
251
            $parentOperator = $siblingsOperator = '-';
252
            $nodeDelta = $rightTo - $siblingsDelta + 1 - $this->node->{$this->leftAttribute};
253
            $newParentUpdateField = $this->leftAttribute;
254
            $oldParentUpdateField = $this->rightAttribute;
255
        } else {
256
            return ['error' => Yii::t('jstw', 'There are two nodes with same "left" value. This should not be.')];
257
        }
258
        $siblingsCondition = [
259
            'and',
260
            ['>=', $this->leftAttribute, $leftFrom],
261
            ['<=', $this->rightAttribute, $rightTo]
262
        ];
263
        $this->applyRootCondition($siblingsCondition);
264
        $db = Yii::$app->getDb();
265
        $transaction = $db->beginTransaction();
266
        $oldParentDepth = $oldParent->{$this->depthAttribute};
267
        $newParentDepth = $this->parent->{$this->depthAttribute};
268 View Code Duplication
        if ($newParentDepth < $oldParentDepth) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
269
            $depthOperator = '-';
270
            $depthDelta = $oldParentDepth - $newParentDepth;
271
        } else {
272
            $depthOperator = '+';
273
            $depthDelta = $newParentDepth - $oldParentDepth;
274
        }
275
        $commonParentsCondition = [
276
            'and',
277
            ['<', $this->leftAttribute, $leftFrom],
278
            ['>', $this->rightAttribute, $rightTo]
279
        ];
280
        $this->applyRootCondition($commonParentsCondition);
281
        $commonParentsIds = $class::find()->select('id')->where($commonParentsCondition)->column();
282
        $commonCondition = [
283
            ['!=', $this->depthAttribute, 0],
284
            ['not in', 'id', $commonParentsIds],
285
        ];
286
        $this->applyRootCondition($commonCondition);
287
        $newParentCondition = array_merge([
288
            'and',
289
            ['<=', $this->leftAttribute, $this->parent->{$this->leftAttribute}],
290
            ['>=', $this->rightAttribute, $this->parent->{$this->rightAttribute}],
291
        ], $commonCondition);
292
        $oldParentsCondition = array_merge([
293
            'and',
294
            ['<', $this->leftAttribute, $this->node->{$this->leftAttribute}],
295
            ['>', $this->rightAttribute, $this->node->{$this->rightAttribute}],
296
        ], $commonCondition);
297
        try {
298
            //updating necessary node siblings
299
            $db->createCommand()->update(
300
                $class::tableName(),
301
                [
302
                    $this->leftAttribute => new Expression($this->leftAttribute . sprintf('%s%d', $siblingsOperator, $siblingsDelta)),
303
                    $this->rightAttribute => new Expression($this->rightAttribute . sprintf('%s%d', $siblingsOperator, $siblingsDelta)),
304
                ],
305
                $siblingsCondition
306
            )->execute();
307
            //updating old parents
308
            $db->createCommand()->update(
309
                $class::tableName(),
310
                [
311
                    //down - right
312
                    $oldParentUpdateField => new Expression($oldParentUpdateField . sprintf('%s%d', $parentOperator, $siblingsDelta)),
313
                ],
314
                $oldParentsCondition
315
            )->execute();
316
            //updating new parents
317
            $db->createCommand()->update(
318
                $class::tableName(),
319
                [
320
                    //down - left
321
                    $newParentUpdateField => new Expression($newParentUpdateField . sprintf('%s%d', $parentOperator, $siblingsDelta)),
322
                ],
323
                $newParentCondition
324
            )->execute();
325
            //updating node with children
326
            $db->createCommand()->update(
327
                $class::tableName(),
328
                [
329
                    $this->leftAttribute => new Expression($this->leftAttribute . sprintf('%s%d', $nodeOperator, $nodeDelta)),
330
                    $this->rightAttribute => new Expression($this->rightAttribute . sprintf('%s%d', $nodeOperator, $nodeDelta)),
331
                    $this->depthAttribute => new Expression($this->depthAttribute . sprintf('%s%d', $depthOperator, $depthDelta)),
332
                ],
333
                ['id' => $nodeChildren]
334
            )->execute();
335
            $transaction->commit();
336
        } catch (\Exception $e) {
337
            $transaction->rollBack();
338
            return ['error' => $e->getMessage()];
339
        }
340
        return true;
341
    }
342
343
    /**
344
     * Moves node between two roots
345
     *
346
     * @param null $position
347
     * @param array $siblings
348
     * @param string | integer $oldParentId
349
     * @return array|bool
0 ignored issues
show
Documentation introduced by
Consider making the return type a bit more specific; maybe use array<string,string>|boolean.

This check looks for the generic type array as a return type and suggests a more specific type. This type is inferred from the actual code.

Loading history...
350
     * @throws \yii\db\Exception
351
     */
352
    protected function moveMultiRoot($position = null, $siblings = [], $oldParentId)
0 ignored issues
show
Coding Style introduced by
Parameters which have default values should be placed at the end.

If you place a parameter with a default value before a parameter with a default value, the default value of the first parameter will never be used as it will always need to be passed anyway:

// $a must always be passed; it's default value is never used.
function someFunction($a = 5, $b) { }
Loading history...
353
    {
354
        $class = $this->className;
355
        if ((int)$oldParentId == 0) {
356
            return ['error' => Yii::t('jstw', 'Can not move root node as child!')];
357
        }
358 View Code Duplication
        if (null === $oldParent = $class::findOne($oldParentId)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
359
            return ['error' => Yii::t('jstw', "Old parent with id '{id}' not found!", ['id' => $oldParentId])];
360
        }
361
        $nodeCountCondition = [
362
            'and',
363
            ['>=', $this->leftAttribute, $this->node->{$this->leftAttribute}],
364
            ['<=', $this->rightAttribute, $this->node->{$this->rightAttribute}],
365
            [$this->rootAttribute => $this->node->{$this->rootAttribute}]
366
        ];
367
        $nodeChildren = $this->getChildIds($nodeCountCondition);
368
        $siblingsDelta = count($nodeChildren) * 2;
369 View Code Duplication
        if ($position == 0) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
370
            $leftFrom = $this->parent->{$this->leftAttribute} + 1;
371
        } else {
372
            if (false === isset($siblings[$position - 1])) {
373
                return ['error' => Yii::t('jstw', 'New previous sibling does not exist')];
374
            }
375
            $newPrevSiblingId = $siblings[$position - 1];
376
            $newPrevSiblingData = $this->getLr($newPrevSiblingId);
377
            $leftFrom = $newPrevSiblingData[$newPrevSiblingId][$this->rightAttribute] + 1;
378
        }
379
        if ($this->node->{$this->leftAttribute} > $leftFrom) {
380
            $nodeDelta = $this->node->{$this->leftAttribute} - $leftFrom;
381
            $nodeOperator = '-';
382
        } else {
383
            $nodeDelta = $leftFrom - $this->node->{$this->leftAttribute};
384
            $nodeOperator = '+';
385
        }
386
        $siblingsCondition = [
387
            'and',
388
            ['>=', $this->leftAttribute, $leftFrom],
389
            [$this->rootAttribute => $this->parent->{$this->rootAttribute}]
390
        ];
391
        $oldSiblingsCondition = [
392
            'and',
393
            ['>', $this->leftAttribute, $this->node->{$this->rightAttribute}],
394
            [$this->rootAttribute => $this->node->{$this->rootAttribute}]
395
        ];
396
        $db = Yii::$app->getDb();
397
        $transaction = $db->beginTransaction();
398
        $oldParentDepth = $oldParent->{$this->depthAttribute};
399
        $newParentDepth = $this->parent->{$this->depthAttribute};
400 View Code Duplication
        if ($newParentDepth < $oldParentDepth) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
401
            $depthOperator = '-';
402
            $depthDelta = $oldParentDepth - $newParentDepth;
403
        } else {
404
            $depthOperator = '+';
405
            $depthDelta = $newParentDepth - $oldParentDepth;
406
        }
407
        $newParentCondition = [
408
            'and',
409
            ['<=', $this->leftAttribute, $this->parent->{$this->leftAttribute}],
410
            ['>=', $this->rightAttribute, $this->parent->{$this->rightAttribute}],
411
            [$this->rootAttribute => $this->parent->{$this->rootAttribute}]
412
        ];
413
        $oldParentsCondition = [
414
            'and',
415
            ['<=', $this->leftAttribute, $oldParent->{$this->leftAttribute}],
416
            ['>=', $this->rightAttribute, $oldParent->{$this->rightAttribute}],
417
            [$this->rootAttribute => $oldParent->{$this->rootAttribute}]
418
        ];
419
        try {
420
            //updating necessary node new siblings
421
            $db->createCommand()->update(
422
                $class::tableName(),
423
                [
424
                    $this->leftAttribute => new Expression($this->leftAttribute . sprintf('+%d', $siblingsDelta)),
425
                    $this->rightAttribute => new Expression($this->rightAttribute . sprintf('+%d', $siblingsDelta)),
426
                ],
427
                $siblingsCondition
428
            )->execute();
429
            //updating necessary node old siblings
430
            $db->createCommand()->update(
431
                $class::tableName(),
432
                [
433
                    $this->leftAttribute => new Expression($this->leftAttribute . sprintf('-%d', $siblingsDelta)),
434
                    $this->rightAttribute => new Expression($this->rightAttribute . sprintf('-%d', $siblingsDelta)),
435
                ],
436
                $oldSiblingsCondition
437
            )->execute();
438
            //updating old parents
439
            $db->createCommand()->update(
440
                $class::tableName(),
441
                [
442
                    $this->rightAttribute => new Expression($this->rightAttribute . sprintf('-%d', $siblingsDelta)),
443
                ],
444
                $oldParentsCondition
445
            )->execute();
446
            //updating new parents
447
            $db->createCommand()->update(
448
                $class::tableName(),
449
                [
450
                    $this->rightAttribute => new Expression($this->rightAttribute . sprintf('+%d', $siblingsDelta)),
451
                ],
452
                $newParentCondition
453
            )->execute();
454
            //updating node with children
455
            $db->createCommand()->update(
456
                $class::tableName(),
457
                [
458
                    $this->leftAttribute => new Expression($this->leftAttribute . sprintf('%s%d', $nodeOperator, $nodeDelta)),
459
                    $this->rightAttribute => new Expression($this->rightAttribute . sprintf('%s%d', $nodeOperator, $nodeDelta)),
460
                    $this->depthAttribute => new Expression($this->depthAttribute . sprintf('%s%d', $depthOperator, $depthDelta)),
461
                    $this->rootAttribute => $this->parent->{$this->rootAttribute}
462
                ],
463
                ['id' => $nodeChildren]
464
            )->execute();
465
            $transaction->commit();
466
        } catch (\Exception $e) {
467
            $transaction->rollBack();
468
            return ['error' => $e->getMessage()];
469
        }
470
        return true;
471
    }
472
473
    /**
474
     * Returns field set of rows to be modified while reordering
475
     *
476
     * @param array $ids
477
     * @return array|\yii\db\ActiveRecord[]
478
     */
479
    protected function getLr($ids)
480
    {
481
        $class = $this->className;
482
        return $class::find()
483
            ->select(['id', $this->leftAttribute, $this->rightAttribute])
484
            ->where(['id' => $ids])
485
            ->indexBy('id')
486
            ->asArray(true)
487
            ->all();
488
    }
489
490
    /**
491
     * Returns count of records to be modified while reordering
492
     *
493
     * @param array $condition
494
     * @return int|string
0 ignored issues
show
Documentation introduced by
Should the return type not be string|null|false?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
495
     */
496
    protected function getCount($condition)
497
    {
498
        $class = $this->className;
499
        return $class::find()
500
            ->select(['id', $this->leftAttribute, $this->rightAttribute, $this->rootAttribute])
501
            ->where($condition)
502
            ->count();
503
    }
504
505
506
    /**
507
     * Returns child ids of selected node
508
     *
509
     * @param array $condition
510
     * @return array
511
     */
512
    protected function getChildIds($condition)
513
    {
514
        $class = $this->className;
515
        return $class::find()
516
            ->select('id')
517
            ->where($condition)
518
            ->column();
519
    }
520
521
    /**
522
     * Applies tree root condition if multi root
523
     *
524
     * @param $condition
525
     */
526
    protected function applyRootCondition(&$condition)
527
    {
528
        if (false !== $this->rootAttribute) {
529
            $condition[] = [$this->rootAttribute => $this->node->{$this->rootAttribute}];
530
        }
531
    }
532
}