Passed
Push — master ( d66cf7...68e8b7 )
by SignpostMarv
02:52
created

ModifyDaftNestedObjectTreeInsertLoose()   C

Complexity

Conditions 9
Paths 13

Size

Total Lines 59
Code Lines 32

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 27
CRAP Score 9

Importance

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