Completed
Push — master ( 362a1e...297fa5 )
by Dmitry
03:01
created

Aggregator::updateReferenceState()   B

Complexity

Conditions 8
Paths 30

Size

Total Lines 59
Code Lines 38

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 59
rs 7.132
c 0
b 0
f 0
cc 8
eloc 38
nc 30
nop 3

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
namespace Tarantool\Mapper\Plugin\Temporal;
4
5
use Tarantool\Mapper\Entity;
6
use Tarantool\Mapper\Plugin\Temporal;
7
8
class Aggregator
9
{
10
    private $temporal;
11
12
    public function __construct(Temporal $temporal)
13
    {
14
        $this->temporal = $temporal;
15
    }
16
17
    public function getLeafs($link)
18
    {
19
        if ($link->timestamp) {
20
            return [$link];
21
        }
22
23
        $leafs = [];
24
        foreach ($this->temporal->getMapper()->find('_temporal_link', ['parent' => $link->id]) as $child) {
25
            foreach ($this->getLeafs($child) as $leaf) {
26
                $leafs[] = $leaf;
27
            }
28
        }
29
        return $leafs;
30
    }
31
32
    public function updateReferenceState($entity, $id, $target)
33
    {
34
        $mapper = $this->temporal->getMapper();
35
36
        $params = [
37
            'entity' => $this->temporal->entityNameToId($entity),
38
            'id'     => $id,
39
            'target' => $this->temporal->entityNameToId($target),
40
        ];
41
42
        $changes = $mapper->find('_temporal_reference', $params);
43
        $states = $this->generateStates($changes, function ($state, $change) {
44
            $state->data = $change->targetId;
45
        });
46
47
        $affected = [];
48
        foreach ($mapper->find('_temporal_reference_state', $params) as $state) {
49
            $mapper->remove($state);
50
        }
51
52
        foreach ($states as $state) {
53
            $entity = $mapper->create('_temporal_reference_state', array_merge($params, [
54
                'begin' => $state->begin,
55
                'end' => $state->end,
56
                'targetId' => $state->data,
57
            ]));
58
            if (!in_array([$entity->target, $entity->targetId], $affected)) {
59
                $affected[] = [$entity->target, $entity->targetId];
60
            }
61
        }
62
63
        foreach ($affected as [$entity, $entityId]) {
64
            $changes = $mapper->find('_temporal_reference_state', [
65
                'target' => $entity,
66
                'targetId' => $entityId,
0 ignored issues
show
Bug introduced by
The variable $entityId does not exist. Did you mean $entity?

This check looks for variables that are accessed but have not been defined. It raises an issue if it finds another variable that has a similar name.

The variable may have been renamed without also renaming all references.

Loading history...
67
                'entity' => $params['entity'],
68
            ]);
69
            $aggregates = $this->generateStates($changes, function ($state, $change) {
70
                if (!in_array($change->id, $state->data)) {
71
                    $state->data[] = $change->id;
72
                }
73
            });
74
            $aggregateParams = [
75
                'entity' => $entity,
76
                'id' => $entityId,
0 ignored issues
show
Bug introduced by
The variable $entityId does not exist. Did you mean $entity?

This check looks for variables that are accessed but have not been defined. It raises an issue if it finds another variable that has a similar name.

The variable may have been renamed without also renaming all references.

Loading history...
77
                'source' => $params['entity']
78
            ];
79
            foreach ($mapper->find('_temporal_reference_aggregate', $aggregateParams) as $aggregate) {
80
                $mapper->remove($aggregate);
81
            }
82
            foreach ($aggregates as $aggregate) {
83
                $mapper->create('_temporal_reference_aggregate', array_merge($aggregateParams, [
84
                    'begin' => $aggregate->begin,
85
                    'end' => $aggregate->end,
86
                    'data' => $aggregate->data,
87
                ]));
88
            }
89
        }
90
    }
91
92
93
    public function updateLinkAggregation(Entity $node)
94
    {
95
        $todo = [
96
            $this->temporal->entityIdToName($node->entity) => $node->entityId,
0 ignored issues
show
Bug introduced by
The property entity does not seem to exist in Tarantool\Mapper\Entity.

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
Bug introduced by
The property entityId does not seem to exist in Tarantool\Mapper\Entity.

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
97
        ];
98
99
        $current = $node;
100
        while ($current->parent) {
101
            $current = $this->temporal->getMapper()->findOne('_temporal_link', ['id' => $current->parent]);
102
            $todo[$this->temporal->entityIdToName($current->entity)] = $current->entityId;
103
        }
104
105
        foreach ($todo as $entity => $id) {
106
            $spaceId = $this->temporal->entityNameToId($entity);
107
            $source = $this->temporal->getMapper()->find('_temporal_link', [
108
                'entity'   => $spaceId,
109
                'entityId' => $id,
110
            ]);
111
112
            $leafs = [];
113
            foreach ($source as $node) {
114
                foreach ($this->getLeafs($node) as $detail) {
115
                    $leafs[] = $detail;
116
                }
117
            }
118
119
            $changeaxis = [];
120
121
            foreach ($leafs as $leaf) {
122
                $current = $leaf;
123
                $ref = [];
124
125
                if (property_exists($leaf, 'idle') && $leaf->idle) {
126
                    continue;
127
                }
128
129
                while ($current) {
130
                    if ($current->entity != $spaceId) {
131
                        $ref[$current->entity] = $current->entityId;
132
                    }
133
                    if ($current->parent) {
134
                        $current = $this->temporal->getMapper()->findOne('_temporal_link', $current->parent);
135
                    } else {
136
                        $current = null;
137
                    }
138
                }
139
140
                $data = [$ref];
141
                if (property_exists($leaf, 'data') && $leaf->data) {
142
                    $data[] = $leaf->data;
143
                }
144
145
                if (!array_key_exists($leaf->timestamp, $changeaxis)) {
146
                    $changeaxis[$leaf->timestamp] = [];
147
                }
148
                $changeaxis[$leaf->timestamp][] = (object) [
149
                    'begin' => $leaf->begin,
150
                    'end' => $leaf->end,
151
                    'data' => $data
152
                ];
153
            }
154
155
            $params = [
156
                'entity' => $spaceId,
157
                'id'     => $id,
158
            ];
159
160
            $timeaxis = [];
161
            foreach ($changeaxis as $timestamp => $changes) {
162
                foreach ($changes as $change) {
163
                    foreach (['begin', 'end'] as $field) {
164
                        if (!array_key_exists($change->$field, $timeaxis)) {
165
                            $timeaxis[$change->$field] = (object) [
166
                                'begin' => $change->$field,
167
                                'end'   => $change->$field,
168
                                'data'  => [],
169
                            ];
170
                        }
171
                    }
172
                }
173
            }
174
175
            ksort($changeaxis);
176
            ksort($timeaxis);
177
178
            $nextSliceId = null;
179
            foreach (array_reverse(array_keys($timeaxis)) as $timestamp) {
180
                if ($nextSliceId) {
181
                    $timeaxis[$timestamp]->end = $nextSliceId;
182
                } else {
183
                    $timeaxis[$timestamp]->end = 0;
184
                }
185
                $nextSliceId = $timestamp;
186
            }
187
188
            foreach ($this->temporal->getMapper()->find('_temporal_link_aggregate', $params) as $state) {
189
                $this->temporal->getMapper()->remove($state);
190
            }
191
192
            $states = [];
193
            foreach ($timeaxis as $state) {
194
                foreach ($changeaxis as $changes) {
195
                    foreach ($changes as $change) {
196
                        if ($change->begin > $state->begin) {
197
                            // future override
198
                            continue;
199
                        }
200
                        if ($change->end && ($change->end < $state->end || !$state->end)) {
201
                            // complete override
202
                            continue;
203
                        }
204
                        $state->data[] = $change->data;
205
                    }
206
                }
207
                if (count($state->data)) {
208
                    $states[] = (object) array_merge(get_object_vars($state), $params);
209
                }
210
            }
211
212
            // merge states
213
            $clean = false;
214
            while (!$clean) {
215
                $clean = true;
216
                foreach ($states as $i => $state) {
217
                    if (array_key_exists($i+1, $states)) {
218
                        $next = $states[$i+1];
219
                        if (json_encode($state->data) == json_encode($next->data)) {
220
                            $states[$i]->end = $next->end;
221
                            unset($states[$i+1]);
222
                            $states = array_values($states);
223
                            $clean = false;
224
                            break;
225
                        }
226
                    }
227
                }
228
            }
229
230
            foreach ($states as $state) {
231
                $this->temporal->getMapper()->create('_temporal_link_aggregate', $state);
232
            }
233
        }
234
    }
235
236
    public function updateOverrideAggregation($entity, $id)
237
    {
238
        $mapper = $this->temporal->getMapper();
239
        $params = [
240
            'entity' => $this->temporal->entityNameToId($entity),
241
            'id'     => $id,
242
        ];
243
244
        $changes = $mapper->find('_temporal_override', $params);
245
        $states = $this->generateStates($changes, function ($state, $change) {
246
            $state->data = array_merge($state->data, $change->data);
247
        });
248
        foreach ($mapper->find('_temporal_override_aggregate', $params) as $aggregate) {
249
            $mapper->remove($aggregate);
250
        }
251
        foreach ($states as $aggregate) {
252
            $mapper->create('_temporal_override_aggregate', array_merge($params, [
253
                'begin' => $aggregate->begin,
254
                'end' => $aggregate->end,
255
                'data' => $aggregate->data,
256
            ]));
257
        }
258
    }
259
260
    private function generateSlices($changes)
0 ignored issues
show
Unused Code introduced by
This method is not used, and could be removed.
Loading history...
261
    {
262
        $slices = [];
263
        foreach ($changes as $change) {
264
            foreach (['begin', 'end'] as $field) {
265
                if (!array_key_exists($change->$field, $slices)) {
266
                    $slices[$change->$field] = (object) [
267
                        'begin'  => $change->$field,
268
                        'end'    => $change->$field,
269
                        'data'   => [],
270
                    ];
271
                }
272
            }
273
        }
274
        ksort($slices);
275
276
        $nextSliceId = null;
277
        foreach (array_reverse(array_keys($slices)) as $timestamp) {
278
            if ($nextSliceId) {
279
                $slices[$timestamp]->end = $nextSliceId;
280
            } else {
281
                $slices[$timestamp]->end = 0;
282
            }
283
            $nextSliceId = $timestamp;
284
        }
285
        return $slices;
286
    }
287
288
    private function generateStates($changes, $callback)
289
    {
290
        $slices = [];
291
        foreach ($changes as $i => $change) {
292
            if (property_exists($change, 'idle') && $change->idle) {
293
                unset($changes[$i]);
294
            }
295
        }
296
        foreach ($changes as $change) {
297
            foreach (['begin', 'end'] as $field) {
298
                if (!array_key_exists($change->$field, $slices)) {
299
                    $slices[$change->$field] = (object) [
300
                        'begin'  => $change->$field,
301
                        'end'    => $change->$field,
302
                        'data'   => [],
303
                    ];
304
                }
305
            }
306
        }
307
        ksort($slices);
308
309
        $nextSliceId = null;
310
        foreach (array_reverse(array_keys($slices)) as $timestamp) {
311
            if ($nextSliceId) {
312
                $slices[$timestamp]->end = $nextSliceId;
313
            } else {
314
                $slices[$timestamp]->end = 0;
315
            }
316
            $nextSliceId = $timestamp;
317
        }
318
319
        // calculate states
320
        $states = [];
321
        foreach ($slices as $slice) {
322
            foreach ($changes as $change) {
323
                if ($change->begin > $slice->begin) {
324
                    // future change
325
                    continue;
326
                }
327
                if ($change->end && ($change->end < $slice->end || !$slice->end)) {
328
                    // complete change
329
                    continue;
330
                }
331
                $callback($slice, $change);
332
            }
333
            if (count($slice->data)) {
334
                $states[] = $slice;
335
            }
336
        }
337
338
        // merge states
339
        $clean = false;
340
        while (!$clean) {
341
            $clean = true;
342
            foreach ($states as $i => $state) {
343
                if (array_key_exists($i+1, $states)) {
344
                    $next = $states[$i+1];
345
                    if (json_encode($state->data) == json_encode($next->data)) {
346
                        $state->end = $next->end;
347
                        unset($states[$i+1]);
348
                        $states = array_values($states);
349
                        $clean = false;
350
                        break;
351
                    }
352
                }
353
            }
354
        }
355
356
        return $states;
357
    }
358
}
359