Passed
Push — master ( 64d761...9bd7b5 )
by SignpostMarv
02:54
created

RebuildTreeInefficiently()   C

Complexity

Conditions 8
Paths 18

Size

Total Lines 122
Code Lines 58

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 51
CRAP Score 8

Importance

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