Passed
Push — master ( 6c8dfe...6e67eb )
by SignpostMarv
03:14
created

TraitWriteableTreeUtilities   A

Complexity

Total Complexity 33

Size/Duplication

Total Lines 301
Duplicated Lines 0 %

Test Coverage

Coverage 0%

Importance

Changes 0
Metric Value
eloc 89
dl 0
loc 301
ccs 0
cts 107
cp 0
rs 9.76
c 0
b 0
f 0
wmc 33

14 Methods

Rating   Name   Duplication   Size   Complexity  
A SiblingsExceptLeaf() 0 19 1
A MaybeRemoveWithPossibleObject() 0 13 2
A ModifyDaftNestedObjectTreeInsertBelow() 0 6 1
A UpdateRoots() 0 9 3
A RebuildAfterInsert() 0 12 2
A ModifyDaftNestedObjectTreeInsertLooseIntoTree() 0 26 3
A RebuildTreeInefficiently() 0 8 1
A MaybeGetLeaf() 0 16 4
A ModifyDaftNestedObjectTreeInsertMaybeLooseIntoTree() 0 17 5
A ModifyDaftNestedObjectTreeInsertAbove() 0 9 1
A ThrowIfNotTree() 0 14 2
A ModifyDaftNestedObjectTreeRemoveWithObjectPrepareRemovalAndRebuild() 0 10 1
A ModifyDaftNestedObjectTreeInsertAdjacent() 0 44 6
A UpdateRemoveThenRebuild() 0 9 1
1
<?php
2
/**
3
* Base daft nested 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
trait TraitWriteableTreeUtilities
16
{
17
    /**
18
    * @param mixed $id
19
    */
20
    abstract public function RecallDaftObject($id) : ? DaftObject;
21
22
    abstract public function CountDaftNestedObjectTreeWithObject(
23
        DaftNestedObject $root,
24
        bool $includeRoot,
25
        ? int $relativeDepthLimit
26
    ) : int;
27
28
    abstract public function RemoveDaftObject(DefinesOwnIdPropertiesInterface $object) : void;
29
30
    abstract public function CountDaftNestedObjectFullTree(int $relativeDepthLimit = null) : int;
31
32
    abstract public function RememberDaftObject(DefinesOwnIdPropertiesInterface $object) : void;
33
34
    abstract public function ForgetDaftObject(DefinesOwnIdPropertiesInterface $object) : void;
35
36
    /**
37
    * @param mixed $id
38
    */
39
    abstract public function ForgetDaftObjectById($id) : void;
40
41
    /**
42
    * @return array<int, DaftNestedObject>
43
    */
44
    abstract public function RecallDaftNestedObjectTreeWithObject(
45
        DaftNestedObject $root,
46
        bool $includeRoot,
47
        ? int $relativeDepthLimit
48
    ) : array;
49
50
    /**
51
    * @return array<int, DaftNestedWriteableObject>
52
    */
53
    abstract public function RecallDaftNestedObjectFullTree(int $relativeDepthLimit = null) : array;
54
55
    /**
56
    * @param mixed $id
57
    *
58
    * @return array<int, DaftNestedWriteableObject>
59
    */
60
    abstract public function RecallDaftNestedObjectTreeWithId(
61
        $id,
62
        bool $includeRoot,
63
        ? int $relativeDepthLimit
64
    ) : array;
65
66
    protected function ModifyDaftNestedObjectTreeInsertMaybeLooseIntoTree(
67
        DaftNestedWriteableObjectTree $tree,
68
        ? DaftNestedWriteableObject $leaf,
69
        ? DaftObject $reference,
70
        bool $isRoot,
71
        bool $before,
72
        ? bool $above
73
    ) : ? DaftNestedWriteableObject {
74
        if ( ! is_null($leaf) && (($reference instanceof DaftNestedWriteableObject) || $isRoot)) {
75
            if ($reference instanceof DaftNestedWriteableObject) {
76
                return $tree->ModifyDaftNestedObjectTreeInsert($leaf, $reference, $before, $above);
77
            }
78
79
            return $this->ModifyDaftNestedObjectTreeInsertLooseIntoTree($leaf, $before, $above);
80
        }
81
82
        return null;
83
    }
84
85
    protected function RebuildAfterInsert(
86
        DaftNestedWriteableObject $newLeaf
87
    ) : DaftNestedWriteableObject {
88
        $this->RebuildTreeInefficiently();
89
90
        $newLeaf = $this->RecallDaftObject($newLeaf->GetId());
91
92
        if ( ! ($newLeaf instanceof DaftNestedWriteableObject)) {
93
            throw new RuntimeException('Could not retrieve leaf from tree after rebuilding!');
94
        }
95
96
        return $newLeaf;
97
    }
98
99
    protected function ModifyDaftNestedObjectTreeRemoveWithObjectPrepareRemovalAndRebuild(
100
        DaftNestedWriteableObject $root,
101
        DaftNestedWriteableObject $replacementRoot
102
    ) : void {
103
        /**
104
        * @var scalar|scalar[] $replacementRootId
105
        */
106
        $replacementRootId = $this->StoreThenRetrieveFreshLeaf($replacementRoot)->GetId();
0 ignored issues
show
Bug introduced by
It seems like StoreThenRetrieveFreshLeaf() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

106
        $replacementRootId = $this->/** @scrutinizer ignore-call */ StoreThenRetrieveFreshLeaf($replacementRoot)->GetId();
Loading history...
107
108
        $this->UpdateRoots($root, $replacementRootId);
109
    }
110
111
    /**
112
    * @param scalar|scalar[] $replacementRootId
113
    */
114
    protected function UpdateRoots(DaftNestedWriteableObject $root, $replacementRootId) : void
115
    {
116
        /**
117
        * @var DaftNestedWriteableObject|null $alter
118
        */
119
        foreach ($this->RecallDaftNestedObjectTreeWithObject($root, false, 1) as $alter) {
120
            if ($alter instanceof DaftNestedWriteableObject) {
121
                $alter->AlterDaftNestedObjectParentId($replacementRootId);
122
                $this->RememberDaftObject($alter);
123
            }
124
        }
125
    }
126
127
    final protected function ThrowIfNotTree() : DaftNestedWriteableObjectTree
128
    {
129
        if ( ! ($this instanceof DaftNestedWriteableObjectTree)) {
130
            throw new BadMethodCallException(
131
                'Cannot call ' .
132
                __FUNCTION__ .
133
                ' on ' .
134
                static::class .
135
                ', class does not implement ' .
136
                DaftNestedWriteableObjectTree::class
137
            );
138
        }
139
140
        return $this;
141
    }
142
143
    /**
144
    * @param DaftNestedWriteableObject|mixed $leaf
145
    */
146
    protected function MaybeGetLeaf($leaf) : ? DaftNestedWriteableObject
147
    {
148
        $tree = $this->ThrowIfNotTree();
149
150
        if ($leaf === $tree->GetNestedObjectTreeRootId()) {
151
            throw new InvalidArgumentException('Cannot pass root id as new leaf');
152
        } elseif ($leaf instanceof DaftNestedWriteableObject) {
153
            return $tree->StoreThenRetrieveFreshLeaf($leaf);
154
        }
155
156
        /**
157
        * @var DaftNestedWriteableObject|null $out
158
        */
159
        $out = $tree->RecallDaftObject($leaf);
160
161
        return ($out instanceof DaftNestedWriteableObject) ? $out : null;
162
    }
163
164
    protected function ModifyDaftNestedObjectTreeInsertLooseIntoTree(
165
        DaftNestedWriteableObject $leaf,
166
        bool $before,
167
        ? bool $above
168
    ) : DaftNestedWriteableObject {
169
        $leaves = $this->RecallDaftNestedObjectFullTree(0);
170
        $leaves = array_filter($leaves, function (DaftNestedWriteableObject $e) use ($leaf) : bool {
171
            return $e->GetId() !== $leaf->GetId();
172
        });
173
        $tree = $this->ThrowIfNotTree();
174
175
        /**
176
        * @var false|DaftNestedWriteableObject $reference
177
        */
178
        $reference = $before ? current($leaves) : end($leaves);
179
180
        if ( ! ($reference instanceof DaftNestedWriteableObject)) {
181
            $leaf->SetIntNestedLeft(0);
182
            $leaf->SetIntNestedRight(1);
183
            $leaf->SetIntNestedLevel(0);
184
            $leaf->AlterDaftNestedObjectParentId($tree->GetNestedObjectTreeRootId());
185
186
            return $tree->StoreThenRetrieveFreshLeaf($leaf);
187
        }
188
189
        return $this->ModifyDaftNestedObjectTreeInsert($leaf, $reference, $before, $above);
0 ignored issues
show
Bug introduced by
The method ModifyDaftNestedObjectTreeInsert() does not exist on SignpostMarv\DaftObject\...tWriteableTreeUtilities. Did you maybe mean ModifyDaftNestedObjectTr...ertMaybeLooseIntoTree()? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

189
        return $this->/** @scrutinizer ignore-call */ ModifyDaftNestedObjectTreeInsert($leaf, $reference, $before, $above);

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
190
    }
191
192
    protected function MaybeRemoveWithPossibleObject(
193
        DaftNestedWriteableObject $rootObject,
194
        ? DaftObject $replacementRootObject
195
    ) : int {
196
        if ( ! ($replacementRootObject instanceof DaftNestedWriteableObject)) {
197
            throw new InvalidArgumentException(
198
                'Could not locate replacement root, cannot leave orphan objects!'
199
            );
200
        }
201
202
        return $this->ModifyDaftNestedObjectTreeRemoveWithObject(
0 ignored issues
show
Bug introduced by
The method ModifyDaftNestedObjectTreeRemoveWithObject() does not exist on SignpostMarv\DaftObject\...tWriteableTreeUtilities. Did you maybe mean ModifyDaftNestedObjectTr...pareRemovalAndRebuild()? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

202
        return $this->/** @scrutinizer ignore-call */ ModifyDaftNestedObjectTreeRemoveWithObject(

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
203
            $rootObject,
204
            $replacementRootObject
205
        );
206
    }
207
208
    /**
209
    * @param scalar|scalar[] $replacementRoot
210
    */
211
    protected function UpdateRemoveThenRebuild(
212
        DaftNestedWriteableObject $rootObject,
213
        $replacementRoot
214
    ) : void {
215
        $this->UpdateRoots($rootObject, $replacementRoot);
216
217
        $this->RemoveDaftObject($rootObject);
218
219
        $this->RebuildTreeInefficiently();
220
    }
221
222
    protected function ModifyDaftNestedObjectTreeInsertAbove(
223
        DaftNestedWriteableObject $newLeaf,
224
        DaftNestedWriteableObject $referenceLeaf
225
    ) : void {
226
        $newLeaf->AlterDaftNestedObjectParentId($referenceLeaf->ObtainDaftNestedObjectParentId());
227
        $referenceLeaf->AlterDaftNestedObjectParentId($newLeaf->GetId());
228
229
        $this->StoreThenRetrieveFreshLeaf($newLeaf);
230
        $this->StoreThenRetrieveFreshLeaf($referenceLeaf);
231
    }
232
233
    protected function ModifyDaftNestedObjectTreeInsertBelow(
234
        DaftNestedWriteableObject $newLeaf,
235
        DaftNestedWriteableObject $referenceLeaf
236
    ) : void {
237
        $newLeaf->AlterDaftNestedObjectParentId($referenceLeaf->GetId());
238
        $this->StoreThenRetrieveFreshLeaf($newLeaf);
239
    }
240
241
    protected function SiblingsExceptLeaf(
242
        DaftNestedWriteableObject $newLeaf,
243
        DaftNestedWriteableObject $referenceLeaf
244
    ) : array {
245
        /**
246
        * @var array<int, DaftNestedWriteableObject> $siblings
247
        */
248
        $siblings = array_values(array_filter(
249
            $this->RecallDaftNestedObjectTreeWithId(
250
                $referenceLeaf->ObtainDaftNestedObjectParentId(),
251
                false,
252
                0
253
            ),
254
            function (DaftNestedWriteableObject $leaf) use ($newLeaf) : bool {
255
                return $leaf->GetId() !== $newLeaf->GetId();
256
            }
257
        ));
258
259
        return $siblings;
260
    }
261
262
    protected function ModifyDaftNestedObjectTreeInsertAdjacent(
263
        DaftNestedWriteableObject $newLeaf,
264
        DaftNestedWriteableObject $referenceLeaf,
265
        bool $before
266
    ) : void {
267
        /**
268
        * @var array<int, DaftNestedWriteableObject> $siblings
269
        */
270
        $siblings = $this->SiblingsExceptLeaf($newLeaf, $referenceLeaf);
271
272
        $siblingIds = [];
273
        $siblingSort = [];
274
        $j = count($siblings);
275
276
        /**
277
        * @var DaftNestedWriteableObject $leaf
278
        */
279
        foreach ($siblings as $leaf) {
280
            /**
281
            * @var scalar|scalar[] $siblingId
282
            */
283
            $siblingId = $leaf->GetId();
284
            $siblingIds[] = $siblingId;
285
            $siblingSort[] = $leaf->GetIntNestedSortOrder();
286
        }
287
288
        $pos = array_search($referenceLeaf->GetId(), $siblingIds, true);
289
290
        if (false === $pos) {
291
            throw new RuntimeException('Reference leaf not found in siblings tree!');
292
        }
293
294
        for ($i = 0; $i < $j; ++$i) {
295
            $siblings[$i]->SetIntNestedSortOrder(
296
                $siblingSort[$i] +
297
                (($before ? ($i < $pos) : ($i <= $pos)) ? -1 : 1)
298
            );
299
            $this->StoreThenRetrieveFreshLeaf($siblings[$i]);
300
        }
301
302
        $newLeaf->SetIntNestedSortOrder($siblingSort[$pos]);
303
        $newLeaf->AlterDaftNestedObjectParentId($referenceLeaf->ObtainDaftNestedObjectParentId());
304
305
        $this->StoreThenRetrieveFreshLeaf($newLeaf);
306
    }
307
308
    protected function RebuildTreeInefficiently() : void
309
    {
310
        /**
311
        * @var DaftNestedWriteableObjectTree $tree
312
        */
313
        $tree = $this->ThrowIfNotTree();
314
        $rebuilder = new InefficientDaftNestedRebuild($tree);
315
        $rebuilder->RebuildTree();
316
    }
317
}
318