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() |
|
|
|
|
44
|
|
|
{ |
45
|
|
|
if (true === empty($this->className) || false === is_subclass_of($this->className, ActiveRecord::class)) { |
|
|
|
|
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(); |
|
|
|
|
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; |
|
|
|
|
99
|
|
|
$this->parent = $parent; |
|
|
|
|
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 |
|
|
|
|
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 |
|
|
|
|
203
|
|
|
* @throws \yii\db\Exception |
204
|
|
|
*/ |
205
|
|
|
protected function move($position = null, $siblings = [], $oldParentId) |
|
|
|
|
206
|
|
|
{ |
207
|
|
|
$class = $this->className; |
208
|
|
View Code Duplication |
if (null === $oldParent = $class::findOne($oldParentId)) { |
|
|
|
|
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) { |
|
|
|
|
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) { |
|
|
|
|
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 |
|
|
|
|
350
|
|
|
* @throws \yii\db\Exception |
351
|
|
|
*/ |
352
|
|
|
protected function moveMultiRoot($position = null, $siblings = [], $oldParentId) |
|
|
|
|
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)) { |
|
|
|
|
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) { |
|
|
|
|
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) { |
|
|
|
|
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 |
|
|
|
|
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
|
|
|
} |
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.