Completed
Push — master ( cc8038...edef00 )
by Bartko
01:27
created

MoveStrategyAbstract::getSourceNodeInfo()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
dl 0
loc 4
ccs 0
cts 4
cp 0
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 0
crap 2
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
    public function __construct(ManipulatorInterface $manipulator)
22
    {
23
        $this->manipulator = $manipulator;
24
    }
25
26
    /**
27
     * {@inheritdoc}
28
     */
29
    public function move($sourceNodeId, $targetNodeId): void
30
    {
31
        $adapter = $this->getManipulator();
32
33
        if ($sourceNodeId == $targetNodeId) {
34
            throw new ValidationException('Cannot move. Source node and Target node are equal.');
35
        }
36
37
        $adapter->beginTransaction();
38
39
        try {
40
            $adapter->lockTree();
41
42
            $sourceNodeInfo = $adapter->getNodeInfo($sourceNodeId);
43
            $targetNodeInfo = $adapter->getNodeInfo($targetNodeId);
44
45
            if (!$sourceNodeInfo) {
46
                throw new ValidationException('Cannot move. Source node does not exists.');
47
            }
48
49
            if (!$targetNodeInfo) {
50
                throw new ValidationException('Cannot move. Target node does not exists.');
51
            }
52
53
            $this->setSourceNodeInfo($sourceNodeInfo);
54
            $this->setTargetNodeInfo($targetNodeInfo);
55
56
            if ($sourceNodeInfo->getScope() != $targetNodeInfo->getScope()) {
57
                throw new ValidationException('Cannot move node between scopes.');
58
            }
59
60
            $this->canMoveBranch();
61
62
            if ($this->isSourceNodeAtRequiredPosition()) {
63
                $adapter->commitTransaction();
64
65
                return;
66
            }
67
68
            $this->updateParentId();
69
            $this->updateLevels();
70
            $this->makeHole();
71
            $this->moveBranchToTheHole();
72
            $this->patchHole();
73
74
            $adapter->commitTransaction();
75
        } catch (\Exception $e) {
76
            $adapter->rollbackTransaction();
77
78
            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
    protected function _updateParentId(NodeInfo $sourceNodeInfo, $newParentId): void
99
    {
100
        if ($sourceNodeInfo->getParentId() != $newParentId) {
101
            $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
    protected function _updateLevels(NodeInfo $sourceNodeInfo, int $levelShift): void
115
    {
116
        if (0 !== $levelShift) {
117
            $this->getManipulator()
118
                ->updateLevels(
119
                    $sourceNodeInfo->getLeft(),
120
                    $sourceNodeInfo->getRight(),
121
                    $levelShift,
122
                    $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
    protected function _makeHole(int $holeFromIndex, int $indexShift, $scope): void
138
    {
139
        $this->getManipulator()->moveLeftIndexes($holeFromIndex, $indexShift, $scope);
140
        $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
    protected function _moveBranchToTheHole(int $leftIndex, int $rightIndex, int $indexShift, $scope): void
155
    {
156
        $this->getManipulator()
157
            ->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
    protected function _patchHole(int $holeFromIndex, int $indexShift, $scope): void
171
    {
172
        $this->getManipulator()
173
            ->moveLeftIndexes($holeFromIndex, $indexShift, $scope);
174
175
        $this->getManipulator()
176
            ->moveRightIndexes($holeFromIndex, $indexShift, $scope);
177
    }
178
179
    /**
180
     * Patch hole.
181
     */
182
    abstract protected function patchHole(): void;
183
184
    /**
185
     * @return int
186
     */
187
    protected function getIndexShift(): int
188
    {
189
        $source = $this->getSourceNodeInfo();
190
191
        return $source->getRight() - $source->getLeft() + 1;
192
    }
193
194
    /**
195
     * @return bool
196
     */
197
    protected function isMovedUp(): bool
198
    {
199
        return ($this->getTargetNodeInfo()->getRight() < $this->getSourceNodeInfo()->getLeft()) ? true : false;
200
    }
201
202
    /**
203
     * @return bool
204
     */
205
    protected function isMovedDown(): bool
206
    {
207
        return ($this->getSourceNodeInfo()->getRight() < $this->getTargetNodeInfo()->getLeft()) ? true : false;
208
    }
209
210
    /**
211
     * @return bool
212
     */
213
    protected function isMovedToRoot(): bool
214
    {
215
        $source = $this->getSourceNodeInfo();
216
        $target = $this->getTargetNodeInfo();
217
218
        return ($source->getLeft() > $target->getLeft() && $source->getRight() < $target->getRight()) ? true : false;
219
    }
220
221
    /**
222
     * @return bool
223
     */
224
    protected function isTargetNodeInsideSourceBranch(): bool
225
    {
226
        $source = $this->getSourceNodeInfo();
227
        $target = $this->getTargetNodeInfo();
228
229
        return ($target->getLeft() > $source->getLeft() && $target->getRight() < $source->getRight()) ? true : false;
230
    }
231
232
    /**
233
     * @return ManipulatorInterface
234
     */
235
    protected function getManipulator(): ManipulatorInterface
236
    {
237
        return $this->manipulator;
238
    }
239
240
    /**
241
     * @return NodeInfo
242
     */
243
    protected function getSourceNodeInfo(): NodeInfo
244
    {
245
        return $this->sourceNodeInfo;
246
    }
247
248
    /**
249
     * @param NodeInfo $sourceNodeInfo
250
     */
251
    private function setSourceNodeInfo(NodeInfo $sourceNodeInfo): void
252
    {
253
        $this->sourceNodeInfo = $sourceNodeInfo;
254
    }
255
256
    /**
257
     * @return NodeInfo
258
     */
259
    protected function getTargetNodeInfo(): NodeInfo
260
    {
261
        return $this->targetNodeInfo;
262
    }
263
264
    /**
265
     * @param NodeInfo $targetNodeInfo
266
     */
267
    private function setTargetNodeInfo(NodeInfo $targetNodeInfo): void
268
    {
269
        $this->targetNodeInfo = $targetNodeInfo;
270
    }
271
}
272