Passed
Push — master ( 6ff084...36d137 )
by SignpostMarv
02:47
created

ModifyDaftNestedObjectTreeInsertAdjacent()   B

Complexity

Conditions 6
Paths 6

Size

Total Lines 53
Code Lines 24

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 24
CRAP Score 6.0023

Importance

Changes 0
Metric Value
cc 6
eloc 24
nc 6
nop 3
dl 0
loc 53
ccs 24
cts 25
cp 0.96
crap 6.0023
rs 8.7155
c 0
b 0
f 0

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
/**
3
* Base daft objects.
4
*
5
* @author SignpostMarv
6
*/
7
declare(strict_types=1);
8
9
namespace SignpostMarv\DaftObject;
10
11
use BadMethodCallException;
12
use InvalidArgumentException;
13
use RuntimeException;
14
15
abstract class DaftWriteableObjectMemoryTree extends DaftObjectMemoryTree implements DaftNestedWriteableObjectTree
16
{
17 10
    public function ModifyDaftNestedObjectTreeInsert(
18
        DaftNestedWriteableObject $newLeaf,
19
        DaftNestedWriteableObject $referenceLeaf,
20
        bool $before = false,
21
        bool $above = null
22
    ) : DaftNestedWriteableObject {
23 10
        if ($newLeaf->GetId() === $referenceLeaf->GetId()) {
24
            throw new InvalidArgumentException('Cannot modify leaf relative to itself!');
25
        }
26
27 10
        if (true === $above) {
28 6
            $this->ModifyDaftNestedObjectTreeInsertAbove($newLeaf, $referenceLeaf);
29 10
        } elseif (false === $above) {
30 2
            $this->ModifyDaftNestedObjectTreeInsertBelow($newLeaf, $referenceLeaf);
31
        } else {
32 10
            $this->ModifyDaftNestedObjectTreeInsertAdjacent($newLeaf, $referenceLeaf, $before);
33
        }
34
35 10
        $this->RebuildTreeInefficiently();
36
37 10
        $newLeaf = $this->RecallDaftObject($newLeaf->GetId());
38
39 10
        if ( ! ($newLeaf instanceof DaftNestedWriteableObject)) {
40
            throw new RuntimeException('Could not retrieve leaf from tree after rebuilding!');
41
        }
42
43 10
        return $newLeaf;
44
    }
45
46 10
    public function ModifyDaftNestedObjectTreeInsertLoose(
47
        $newLeaf,
48
        $referenceLeafId,
49
        bool $before = false,
50
        bool $above = null
51
    ) : DaftNestedWriteableObject {
52 10
        if ($newLeaf === $this->GetNestedObjectTreeRootId()) {
53
            throw new InvalidArgumentException('Cannot pass root id as new leaf');
54
        }
55
56
        $newLeaf = (
57 10
            ($newLeaf instanceof DaftNestedWriteableObject)
58 10
                ? $newLeaf
59 10
                : $this->RecallDaftObject($newLeaf)
60
        );
61 10
        $referenceLeaf = $this->RecallDaftObject($referenceLeafId);
62
63 10
        if ( ! ($newLeaf instanceof DaftNestedWriteableObject)) {
64
            throw new InvalidArgumentException(
65
                'Arguemnt 1 passed to ' .
66
                __METHOD__ .
67
                ' did not resolve to a leaf node!'
68
            );
69
        }
70
71 10
        $newLeaf = $this->StoreThenRetrieveFreshCopy($newLeaf);
72
73
        if (
74 10
            ($newLeaf instanceof DaftNestedWriteableObject) &&
75 10
            ($referenceLeaf instanceof DaftNestedWriteableObject)
76
        ) {
77 4
            return $this->ModifyDaftNestedObjectTreeInsert(
78 4
                $newLeaf,
79 4
                $referenceLeaf,
80 4
                $before,
81 4
                $above
82
            );
83 10
        } elseif ($referenceLeafId !== $this->GetNestedObjectTreeRootId()) {
84
            throw new InvalidArgumentException(
85
                'Arguemnt 2 passed to ' .
86
                __METHOD__ .
87
                ' did not resolve to a leaf node!'
88
            );
89
        }
90
91 10
        $tree = array_filter(
92 10
            $this->RecallDaftNestedObjectFullTree(0),
93
            function (DaftNestedWriteableObject $leaf) use ($newLeaf) : bool {
94 10
                return $leaf->GetId() !== $newLeaf->GetId();
95 10
            }
96
        );
97
98 10
        if (0 === count($tree)) {
99 10
            $newLeaf->SetIntNestedLeft(0);
100 10
            $newLeaf->SetIntNestedRight(1);
101 10
            $newLeaf->SetIntNestedLevel(0);
102 10
            $newLeaf->AlterDaftNestedObjectParentId($this->GetNestedObjectTreeRootId());
103
104 10
            return $this->StoreThenRetrieveFreshCopy($newLeaf);
105
        }
106
107
        /**
108
        * @var DaftNestedWriteableObject $referenceLeaf
109
        */
110 10
        $referenceLeaf = $before ? current($tree) : end($tree);
111
112 10
        return $this->ModifyDaftNestedObjectTreeInsert($newLeaf, $referenceLeaf, $before, $above);
113
    }
114
115 2
    public function ModifyDaftNestedObjectTreeRemoveWithObject(
116
        DaftNestedWriteableObject $root,
117
        ? DaftNestedWriteableObject $replacementRoot
118
    ) : int {
119
        if (
120 2
            $this->CountDaftNestedObjectTreeWithObject($root, false, null) > 0 &&
121 2
            is_null($replacementRoot)
122
        ) {
123
            throw new BadMethodCallException('Cannot leave orphan objects in a tree');
124
        }
125
126 2
        $root = $this->StoreThenRetrieveFreshCopy($root);
127
128 2
        if ( ! is_null($replacementRoot)) {
129
            $replacementRoot = $this->StoreThenRetrieveFreshCopy($replacementRoot);
130
131
            /**
132
            * @var DaftNestedWriteableObject $alter
133
            */
134
            foreach ($this->RecallDaftNestedObjectTreeWithObject($root, false, 0) as $alter) {
135
                $alter->AlterDaftNestedObjectParentId($root->ObtainDaftNestedObjectParentId());
136
                $alter = $this->StoreThenRetrieveFreshCopy($alter);
137
                if ($alter->GetId() === $replacementRoot->GetId()) {
138
                    $replacementRoot = $alter;
139
                }
140
            }
141
        }
142
143 2
        $this->RemoveDaftObject($root);
144
145 2
        $this->RebuildTreeInefficiently();
146
147 2
        return $this->CountDaftNestedObjectFullTree();
148
    }
149
150
    /**
151
    * {@inheritdoc}
152
    */
153 2
    public function ModifyDaftNestedObjectTreeRemoveWithId($root, $replacementRoot) : int
154
    {
155 2
        $rootObject = $this->RecallDaftObject($root);
156
157 2
        if ( ! ($rootObject instanceof DaftNestedWriteableObject)) {
158 2
            return $this->CountDaftNestedObjectFullTree();
159
        }
160
161
        if (
162 2
            ! is_null($replacementRoot) &&
163 2
            $replacementRoot !== $this->GetNestedObjectTreeRootId()
164
        ) {
165
            $replacementRootObject = $this->RecallDaftObject($replacementRoot);
166
167
            if ( ! ($replacementRootObject instanceof DaftNestedWriteableObject)) {
168
                throw new InvalidArgumentException(
169
                    'Could not locate replacement root, cannot leave orphan objects!'
170
                );
171
            }
172
173
            return $this->ModifyDaftNestedObjectTreeRemoveWithObject(
174
                $rootObject,
175
                $replacementRootObject
176
            );
177
        }
178
179
        if (
180 2
            $this->CountDaftNestedObjectTreeWithObject($rootObject, false, null) > 0 &&
181 2
            is_null($replacementRoot)
182
        ) {
183
            throw new BadMethodCallException('Cannot leave orphan objects in a tree');
184
        }
185
186
        /**
187
        * @var DaftNestedWriteableObject $alter
188
        */
189 2
        foreach ($this->RecallDaftNestedObjectTreeWithObject($rootObject, false, null) as $alter) {
190
            $alter = $this->StoreThenRetrieveFreshCopy($alter);
191
            $alter->AlterDaftNestedObjectParentId($replacementRoot);
192
            $this->RememberDaftObject($alter);
193
        }
194
195 2
        $this->RemoveDaftObject($rootObject);
196
197 2
        $this->RebuildTreeInefficiently();
198
199 2
        return $this->CountDaftNestedObjectFullTree();
200
    }
201
202 6
    protected function ModifyDaftNestedObjectTreeInsertAbove(
203
        DaftNestedWriteableObject $newLeaf,
204
        DaftNestedWriteableObject $referenceLeaf
205
    ) : void {
206 6
        $newLeaf->AlterDaftNestedObjectParentId($referenceLeaf->ObtainDaftNestedObjectParentId());
207 6
        $referenceLeaf->AlterDaftNestedObjectParentId($newLeaf->GetId());
208
209 6
        $this->StoreThenRetrieveFreshCopy($newLeaf);
210 6
        $this->StoreThenRetrieveFreshCopy($referenceLeaf);
211 6
    }
212
213 2
    protected function ModifyDaftNestedObjectTreeInsertBelow(
214
        DaftNestedWriteableObject $newLeaf,
215
        DaftNestedWriteableObject $referenceLeaf
216
    ) : void {
217 2
        $newLeaf->AlterDaftNestedObjectParentId($referenceLeaf->GetId());
218 2
        $this->StoreThenRetrieveFreshCopy($newLeaf);
219 2
    }
220
221 10
    protected function ModifyDaftNestedObjectTreeInsertAdjacent(
222
        DaftNestedWriteableObject $newLeaf,
223
        DaftNestedWriteableObject $referenceLeaf,
224
        bool $before
225
    ) : void {
226
        /**
227
        * @var array<int, DaftNestedWriteableObject> $siblings
228
        */
229 10
        $siblings = array_values(array_filter(
230 10
            $this->RecallDaftNestedObjectTreeWithId(
231 10
                $referenceLeaf->ObtainDaftNestedObjectParentId(),
232 10
                false,
233
                0
234
            ),
235
            function (DaftNestedWriteableObject $leaf) use ($newLeaf) : bool {
236 10
                return $leaf->GetId() !== $newLeaf->GetId();
237 10
            }
238
        ));
239
240 10
        $siblingIds = [];
241 10
        $siblingSort = [];
242 10
        $j = count($siblings);
243
244
        /**
245
        * @var DaftNestedWriteableObject $leaf
246
        */
247 10
        foreach ($siblings as $leaf) {
248
            /**
249
            * @var scalar|scalar[] $siblingId
250
            */
251 10
            $siblingId = $leaf->GetId();
252 10
            $siblingIds[] = $siblingId;
253 10
            $siblingSort[] = $leaf->GetIntNestedSortOrder();
254
        }
255
256 10
        $pos = array_search($referenceLeaf->GetId(), $siblingIds, true);
257
258 10
        if (false === $pos) {
259
            throw new RuntimeException('Reference leaf not found in siblings tree!');
260
        }
261
262 10
        for ($i = 0; $i < $j; ++$i) {
263 10
            $siblings[$i]->SetIntNestedSortOrder(
264 10
                $siblingSort[$i] +
265 10
                (($before ? ($i < $pos) : ($i <= $pos)) ? -1 : 1)
266
            );
267 10
            $this->StoreThenRetrieveFreshCopy($siblings[$i]);
268
        }
269
270 10
        $newLeaf->SetIntNestedSortOrder($siblingSort[$pos]);
271 10
        $newLeaf->AlterDaftNestedObjectParentId($referenceLeaf->ObtainDaftNestedObjectParentId());
272
273 10
        $this->StoreThenRetrieveFreshCopy($newLeaf);
274 10
    }
275
276 12
    protected function RememberDaftObjectData(DefinesOwnIdPropertiesInterface $object) : void
277
    {
278 12
        static::ThrowIfNotType($object, DaftNestedWriteableObject::class, 1, __METHOD__);
279
280 12
        parent::RememberDaftObjectData($object);
281 12
    }
282
283
    /**
284
    * @param DaftObject|string $object
285
    */
286 14
    protected static function ThrowIfNotType(
287
        $object,
288
        string $type,
289
        int $argument,
290
        string $function
291
    ) : void {
292 14
        if ( ! is_a($object, DaftNestedWriteableObject::class, is_string($object))) {
293 2
            throw new DaftObjectRepositoryTypeByClassMethodAndTypeException(
294 2
                $argument,
295 2
                static::class,
296 2
                $function,
297 2
                DaftNestedWriteableObject::class,
298 2
                is_string($object) ? $object : get_class($object)
299
            );
300
        }
301
302 12
        parent::ThrowIfNotType($object, $type, $argument, $function);
303 12
    }
304
305 10
    protected function RebuildTreeInefficiently() : void
306
    {
307
        $parentIdXref = [
308 10
            (array) $this->GetNestedObjectTreeRootId(),
309
        ];
310
311
        /**
312
        * @var array<int, array<int, DaftNestedWriteableObject>> $xRefChildren
313
        */
314
        $xRefChildren = [
315 10
            [],
316
        ];
317
318
        /**
319
        * @var array<int, scalar|scalar[]> $idXref
320
        */
321 10
        $idXref = [];
322
323 10
        $tree = $this->RecallDaftNestedObjectFullTree();
324
325
        usort($tree, function (DaftNestedWriteableObject $a, DaftNestedWriteableObject $b) : int {
326 10
            return $this->CompareObjects($a, $b);
327 10
        });
328
329
        /**
330
        * @var DaftNestedWriteableObject $leaf
331
        */
332 10
        foreach ($tree as $i => $leaf) {
333 10
            $leafParentId = $leaf->ObtainDaftNestedObjectParentId();
334 10
            $pos = array_search($leafParentId, $parentIdXref, true);
335
336 10
            if (false === $pos) {
337 8
                $parentIdXref[] = $leafParentId;
338
339
                /**
340
                * @var int $pos
341
                */
342 8
                $pos = array_search($leafParentId, $parentIdXref, true);
343
344 8
                $xRefChildren[$pos] = [];
345
            }
346
347 10
            if ( ! in_array($leaf, $xRefChildren[$pos], true)) {
348 10
                $xRefChildren[$pos][] = $leaf;
349
            }
350
351 10
            if ( ! in_array($leaf->GetId(), $idXref, true)) {
352
                /**
353
                * @var scalar|scalar[] $leafId
354
                */
355 10
                $leafId = $leaf->GetId();
356 10
                $idXref[] = $leafId;
357
            }
358
359 10
            $leaf->SetIntNestedLeft(0);
360 10
            $leaf->SetIntNestedRight(0);
361 10
            $leaf->SetIntNestedLevel(0);
362
363 10
            $tree[$i] = $this->StoreThenRetrieveFreshCopy($leaf);
364
        }
365
366
        $rebuild = function (
367
            DaftNestedWriteableObject $leaf,
368
            int $level,
369
            int $n,
370
            array $parentIds,
371
            array $ids,
372
            array $children
373
        ) use (
374 10
            &$rebuild
375
        ) : int {
376
            /**
377
            * @var scalar|scalar[] $id
378
            */
379 10
            $id = $leaf->GetId();
380
381 10
            $leaf->SetIntNestedLevel($level);
382 10
            $leaf->SetIntNestedLeft($n);
383
384 10
            ++$n;
385
386
            /**
387
            * @var int|false $parentPos
388
            */
389 10
            $parentPos = array_search((array) $id, $parentIds, true);
390
391 10
            if (false !== $parentPos) {
392
                /**
393
                * @var DaftNestedWriteableObject $childLeaf
394
                */
395 8
                foreach ($children[$parentPos] as $childLeaf) {
396 8
                    $n = (int) $rebuild(
397 8
                        $childLeaf,
398 8
                        $level + 1,
399 8
                        $n,
400 8
                        $parentIds,
401 8
                        $ids,
402 8
                        $children
403
                    );
404
                }
405
            }
406
407 10
            $leaf->SetIntNestedRight($n);
408
409 10
            $this->StoreThenRetrieveFreshCopy($leaf);
410
411 10
            return $n + 1;
412 10
        };
413
414 10
        $n = 0;
415
416
        /**
417
        * @var DaftNestedWriteableObject $rootLeaf
418
        */
419 10
        foreach ($xRefChildren[0] as $rootLeaf) {
420 10
            $n = $rebuild(
421 10
                $rootLeaf,
422 10
                0,
423 10
                $n,
424 10
                $parentIdXref,
425 10
                $idXref,
426 10
                $xRefChildren
427
            );
428
        }
429 10
    }
430
431 10
    protected function StoreThenRetrieveFreshCopy(
432
        DaftNestedWriteableObject $leaf
433
    ) : DaftNestedWriteableObject {
434 10
        $this->RememberDaftObject($leaf);
435 10
        $this->ForgetDaftObject($leaf);
436 10
        $this->ForgetDaftObjectById($leaf->GetId());
437
438 10
        $fresh = $this->RecallDaftObject($leaf->GetId());
439
440 10
        if ( ! ($fresh instanceof DaftNestedWriteableObject)) {
441
            throw new RuntimeException('Was not able to obtain a fresh copy of the object!');
442
        }
443
444 10
        return $fresh;
445
    }
446
}
447