MoveStrategyAbstract   A
last analyzed

Complexity

Total Complexity 31

Size/Duplication

Total Lines 259
Duplicated Lines 0 %

Test Coverage

Coverage 100%

Importance

Changes 0
Metric Value
eloc 66
c 0
b 0
f 0
dl 0
loc 259
rs 9.92
ccs 77
cts 77
cp 1
wmc 31

17 Methods

Rating   Name   Duplication   Size   Complexity  
A getManipulator() 0 3 1
A setTargetNodeInfo() 0 3 1
A isMovedUp() 0 3 2
A getTargetNodeInfo() 0 3 1
A getIndexShift() 0 5 1
A isTargetNodeInsideSourceBranch() 0 6 3
A _updateLevels() 0 9 2
A _updateParentId() 0 4 2
A _makeHole() 0 4 1
A getSourceNodeInfo() 0 3 1
B move() 0 50 7
A isMovedToRoot() 0 6 3
A _patchHole() 0 7 1
A __construct() 0 3 1
A isMovedDown() 0 3 2
A setSourceNodeInfo() 0 3 1
A _moveBranchToTheHole() 0 4 1
1
<?php
2
3
declare(strict_types=1);
4
5
namespace StefanoTree\NestedSet\MoveStrategy;
6
7
use StefanoTree\Exception\ValidationException;
8
use StefanoTree\NestedSet\Manipulator\ManipulatorInterface;
9
use StefanoTree\NestedSet\NodeInfo;
10
11
abstract class MoveStrategyAbstract implements MoveStrategyInterface
12
{
13
    private $manipulator;
14
15
    private $sourceNodeInfo;
16
    private $targetNodeInfo;
17
18
    /**
19
     * @param ManipulatorInterface $manipulator
20
     */
21 12
    public function __construct(ManipulatorInterface $manipulator)
22
    {
23 12
        $this->manipulator = $manipulator;
24
    }
25
26
    /**
27
     * {@inheritdoc}
28
     */
29 12
    public function move($sourceNodeId, $targetNodeId): void
30
    {
31 12
        $adapter = $this->getManipulator();
32
33 12
        if ($sourceNodeId == $targetNodeId) {
34 1
            throw new ValidationException('Cannot move. Source node and Target node are equal.');
35
        }
36
37 11
        $adapter->beginTransaction();
38
39
        try {
40 11
            $adapter->lockTree();
41
42 11
            $sourceNodeInfo = $adapter->getNodeInfo($sourceNodeId);
43 11
            $targetNodeInfo = $adapter->getNodeInfo($targetNodeId);
44
45 11
            if (!$sourceNodeInfo) {
46 1
                throw new ValidationException('Cannot move. Source node does not exists.');
47
            }
48
49 10
            if (!$targetNodeInfo) {
50 1
                throw new ValidationException('Cannot move. Target node does not exists.');
51
            }
52
53 9
            $this->setSourceNodeInfo($sourceNodeInfo);
54 9
            $this->setTargetNodeInfo($targetNodeInfo);
55
56 9
            if ($sourceNodeInfo->getScope() != $targetNodeInfo->getScope()) {
57 1
                throw new ValidationException('Cannot move node between scopes.');
58
            }
59
60 8
            $this->canMoveBranch();
61
62 5
            if ($this->isSourceNodeAtRequiredPosition()) {
63 4
                $adapter->commitTransaction();
64
65 4
                return;
66
            }
67
68 5
            $this->updateParentId();
69 5
            $this->updateLevels();
70 5
            $this->makeHole();
71 5
            $this->moveBranchToTheHole();
72 5
            $this->patchHole();
73
74 5
            $adapter->commitTransaction();
75 6
        } catch (\Exception $e) {
76 6
            $adapter->rollbackTransaction();
77
78 6
            throw $e;
79
        }
80
    }
81
82
    /**
83
     * Check if can move node.
84
     *
85
     * @throws ValidationException if cannot move branch
86
     */
87
    abstract protected function canMoveBranch(): void;
88
89
    /**
90
     * @return bool
91
     */
92
    abstract protected function isSourceNodeAtRequiredPosition(): bool;
93
94
    /**
95
     * @param NodeInfo        $sourceNodeInfo
96
     * @param null|int|string $newParentId
97
     */
98 5
    protected function _updateParentId(NodeInfo $sourceNodeInfo, $newParentId): void
99
    {
100 5
        if ($sourceNodeInfo->getParentId() != $newParentId) {
101 4
            $this->getManipulator()->updateParentId($sourceNodeInfo->getId(), $newParentId);
102
        }
103
    }
104
105
    /**
106
     * Update parent id.
107
     */
108
    abstract protected function updateParentId(): void;
109
110
    /**
111
     * @param NodeInfo $sourceNodeInfo
112
     * @param int      $levelShift
113
     */
114 5
    protected function _updateLevels(NodeInfo $sourceNodeInfo, int $levelShift): void
115
    {
116 5
        if (0 !== $levelShift) {
117 4
            $this->getManipulator()
118 4
                ->updateLevels(
119 4
                    $sourceNodeInfo->getLeft(),
120 4
                    $sourceNodeInfo->getRight(),
121
                    $levelShift,
122 4
                    $sourceNodeInfo->getScope()
123
                );
124
        }
125
    }
126
127
    /**
128
     * Update levels.
129
     */
130
    abstract protected function updateLevels(): void;
131
132
    /**
133
     * @param int             $holeFromIndex
134
     * @param int             $indexShift
135
     * @param null|int|string $scope
136
     */
137 5
    protected function _makeHole(int $holeFromIndex, int $indexShift, $scope): void
138
    {
139 5
        $this->getManipulator()->moveLeftIndexes($holeFromIndex, $indexShift, $scope);
140 5
        $this->getManipulator()->moveRightIndexes($holeFromIndex, $indexShift, $scope);
141
    }
142
143
    /**
144
     * Make hole for moved branch.
145
     */
146
    abstract protected function makeHole(): void;
147
148
    /**
149
     * @param int             $leftIndex
150
     * @param int             $rightIndex
151
     * @param int             $indexShift
152
     * @param null|int|string $scope
153
     */
154 5
    protected function _moveBranchToTheHole(int $leftIndex, int $rightIndex, int $indexShift, $scope): void
155
    {
156 5
        $this->getManipulator()
157 5
            ->moveBranch($leftIndex, $rightIndex, $indexShift, $scope);
158
    }
159
160
    /**
161
     * Move branch to the Hole.
162
     */
163
    abstract protected function moveBranchToTheHole(): void;
164
165
    /**
166
     * @param int             $holeFromIndex
167
     * @param int             $indexShift
168
     * @param null|int|string $scope
169
     */
170 5
    protected function _patchHole(int $holeFromIndex, int $indexShift, $scope): void
171
    {
172 5
        $this->getManipulator()
173 5
            ->moveLeftIndexes($holeFromIndex, $indexShift, $scope);
174
175 5
        $this->getManipulator()
176 5
            ->moveRightIndexes($holeFromIndex, $indexShift, $scope);
177
    }
178
179
    /**
180
     * Patch hole.
181
     */
182
    abstract protected function patchHole(): void;
183
184
    /**
185
     * @return int
186
     */
187 5
    protected function getIndexShift(): int
188
    {
189 5
        $source = $this->getSourceNodeInfo();
190
191 5
        return $source->getRight() - $source->getLeft() + 1;
192
    }
193
194
    /**
195
     * @return bool
196
     */
197 4
    protected function isMovedUp(): bool
198
    {
199 4
        return ($this->getTargetNodeInfo()->getRight() < $this->getSourceNodeInfo()->getLeft()) ? true : false;
200
    }
201
202
    /**
203
     * @return bool
204
     */
205 5
    protected function isMovedDown(): bool
206
    {
207 5
        return ($this->getSourceNodeInfo()->getRight() < $this->getTargetNodeInfo()->getLeft()) ? true : false;
208
    }
209
210
    /**
211
     * @return bool
212
     */
213 5
    protected function isMovedToRoot(): bool
214
    {
215 5
        $source = $this->getSourceNodeInfo();
216 5
        $target = $this->getTargetNodeInfo();
217
218 5
        return ($source->getLeft() > $target->getLeft() && $source->getRight() < $target->getRight()) ? true : false;
219
    }
220
221
    /**
222
     * @return bool
223
     */
224 8
    protected function isTargetNodeInsideSourceBranch(): bool
225
    {
226 8
        $source = $this->getSourceNodeInfo();
227 8
        $target = $this->getTargetNodeInfo();
228
229 8
        return ($target->getLeft() > $source->getLeft() && $target->getRight() < $source->getRight()) ? true : false;
230
    }
231
232
    /**
233
     * @return ManipulatorInterface
234
     */
235 12
    protected function getManipulator(): ManipulatorInterface
236
    {
237 12
        return $this->manipulator;
238
    }
239
240
    /**
241
     * @return NodeInfo
242
     */
243 8
    protected function getSourceNodeInfo(): NodeInfo
244
    {
245 8
        return $this->sourceNodeInfo;
246
    }
247
248
    /**
249
     * @param NodeInfo $sourceNodeInfo
250
     */
251 9
    private function setSourceNodeInfo(NodeInfo $sourceNodeInfo): void
252
    {
253 9
        $this->sourceNodeInfo = $sourceNodeInfo;
254
    }
255
256
    /**
257
     * @return NodeInfo
258
     */
259 8
    protected function getTargetNodeInfo(): NodeInfo
260
    {
261 8
        return $this->targetNodeInfo;
262
    }
263
264
    /**
265
     * @param NodeInfo $targetNodeInfo
266
     */
267 9
    private function setTargetNodeInfo(NodeInfo $targetNodeInfo): void
268
    {
269 9
        $this->targetNodeInfo = $targetNodeInfo;
270
    }
271
}
272