Completed
Push — master ( ee2588...6f9f53 )
by Dmitry
03:53 queued 01:28
created

Aggregator::generateSlices()   B

Complexity

Conditions 6
Paths 12

Size

Total Lines 27
Code Lines 18

Duplication

Lines 19
Ratio 70.37 %

Importance

Changes 0
Metric Value
dl 19
loc 27
rs 8.439
c 0
b 0
f 0
cc 6
eloc 18
nc 12
nop 1
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 $affect) {
64
            list($entity, $entityId) = $affect;
65
            $changes = $mapper->find('_temporal_reference_state', [
66
                'target' => $entity,
67
                'targetId' => $entityId,
68
                'entity' => $params['entity'],
69
            ]);
70
            $aggregates = $this->generateStates($changes, function ($state, $change) {
71
                if (!in_array($change->id, $state->data)) {
72
                    $state->data[] = $change->id;
73
                }
74
            });
75
            $aggregateParams = [
76
                'entity' => $entity,
77
                'id' => $entityId,
78
                'source' => $params['entity']
79
            ];
80
            foreach ($mapper->find('_temporal_reference_aggregate', $aggregateParams) as $aggregate) {
81
                $mapper->remove($aggregate);
82
            }
83
            foreach ($aggregates as $aggregate) {
84
                $mapper->create('_temporal_reference_aggregate', array_merge($aggregateParams, [
85
                    'begin' => $aggregate->begin,
86
                    'end' => $aggregate->end,
87
                    'data' => $aggregate->data,
88
                ]));
89
            }
90
        }
91
    }
92
93
94
    public function updateLinkAggregation(Entity $node)
95
    {
96
        $todo = [
97
            $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...
98
        ];
99
100
        $current = $node;
101
        while ($current->parent) {
102
            $current = $this->temporal->getMapper()->findOne('_temporal_link', ['id' => $current->parent]);
103
            $todo[$this->temporal->entityIdToName($current->entity)] = $current->entityId;
104
        }
105
106
        foreach ($todo as $entity => $id) {
107
            $spaceId = $this->temporal->entityNameToId($entity);
108
            $source = $this->temporal->getMapper()->find('_temporal_link', [
109
                'entity'   => $spaceId,
110
                'entityId' => $id,
111
            ]);
112
113
            $leafs = [];
114
            foreach ($source as $node) {
115
                foreach ($this->getLeafs($node) as $detail) {
116
                    $leafs[] = $detail;
117
                }
118
            }
119
120
            $changeaxis = [];
121
122
            foreach ($leafs as $leaf) {
123
                $current = $leaf;
124
                $ref = [];
125
126
                if (property_exists($leaf, 'idle') && $leaf->idle) {
127
                    continue;
128
                }
129
130
                while ($current) {
131
                    if ($current->entity != $spaceId) {
132
                        $ref[$current->entity] = $current->entityId;
133
                    }
134
                    if ($current->parent) {
135
                        $current = $this->temporal->getMapper()->findOne('_temporal_link', $current->parent);
136
                    } else {
137
                        $current = null;
138
                    }
139
                }
140
141
                $data = [$ref];
142
                if (property_exists($leaf, 'data') && $leaf->data) {
143
                    $data[] = $leaf->data;
144
                }
145
146
                if (!array_key_exists($leaf->timestamp, $changeaxis)) {
147
                    $changeaxis[$leaf->timestamp] = [];
148
                }
149
                $changeaxis[$leaf->timestamp][] = (object) [
150
                    'begin' => $leaf->begin,
151
                    'end' => $leaf->end,
152
                    'data' => $data
153
                ];
154
            }
155
156
            $params = [
157
                'entity' => $spaceId,
158
                'id'     => $id,
159
            ];
160
161
            $timeaxis = [];
162
            foreach ($changeaxis as $timestamp => $changes) {
163 View Code Duplication
                foreach ($changes as $change) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
164
                    foreach (['begin', 'end'] as $field) {
165
                        if (!array_key_exists($change->$field, $timeaxis)) {
166
                            $timeaxis[$change->$field] = (object) [
167
                                'begin' => $change->$field,
168
                                'end'   => $change->$field,
169
                                'data'  => [],
170
                            ];
171
                        }
172
                    }
173
                }
174
            }
175
176
            ksort($changeaxis);
177
            ksort($timeaxis);
178
179
            $nextSliceId = null;
180 View Code Duplication
            foreach (array_reverse(array_keys($timeaxis)) as $timestamp) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
181
                if ($nextSliceId) {
182
                    $timeaxis[$timestamp]->end = $nextSliceId;
183
                } else {
184
                    $timeaxis[$timestamp]->end = 0;
185
                }
186
                $nextSliceId = $timestamp;
187
            }
188
189
            foreach ($this->temporal->getMapper()->find('_temporal_link_aggregate', $params) as $state) {
190
                $this->temporal->getMapper()->remove($state);
191
            }
192
193
            $states = [];
194
            foreach ($timeaxis as $state) {
195 View Code Duplication
                foreach ($changeaxis as $changes) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
196
                    foreach ($changes as $change) {
197
                        if ($change->begin > $state->begin) {
198
                            // future override
199
                            continue;
200
                        }
201
                        if ($change->end && ($change->end < $state->end || !$state->end)) {
202
                            // complete override
203
                            continue;
204
                        }
205
                        $state->data[] = $change->data;
206
                    }
207
                }
208
                if (count($state->data)) {
209
                    $states[] = (object) array_merge(get_object_vars($state), $params);
210
                }
211
            }
212
213
            // merge states
214
            $clean = false;
215 View Code Duplication
            while (!$clean) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
216
                $clean = true;
217
                foreach ($states as $i => $state) {
218
                    if (array_key_exists($i+1, $states)) {
219
                        $next = $states[$i+1];
220
                        if (json_encode($state->data) == json_encode($next->data)) {
221
                            $states[$i]->end = $next->end;
222
                            unset($states[$i+1]);
223
                            $states = array_values($states);
224
                            $clean = false;
225
                            break;
226
                        }
227
                    }
228
                }
229
            }
230
231
            foreach ($states as $state) {
232
                $this->temporal->getMapper()->create('_temporal_link_aggregate', $state);
233
            }
234
        }
235
    }
236
237
    public function updateOverrideAggregation($entity, $id)
238
    {
239
        $mapper = $this->temporal->getMapper();
240
        $params = [
241
            'entity' => $this->temporal->entityNameToId($entity),
242
            'id'     => $id,
243
        ];
244
245
        $changes = $mapper->find('_temporal_override', $params);
246
        $states = $this->generateStates($changes, function ($state, $change) {
247
            $state->data = array_merge($state->data, $change->data);
248
        });
249
        foreach ($mapper->find('_temporal_override_aggregate', $params) as $aggregate) {
250
            $mapper->remove($aggregate);
251
        }
252
        foreach ($states as $aggregate) {
253
            $mapper->create('_temporal_override_aggregate', array_merge($params, [
254
                'begin' => $aggregate->begin,
255
                'end' => $aggregate->end,
256
                'data' => $aggregate->data,
257
            ]));
258
        }
259
    }
260
261
    private function generateStates($changes, $callback)
262
    {
263
        $slices = [];
264
        foreach ($changes as $i => $change) {
265
            if (property_exists($change, 'idle') && $change->idle) {
266
                unset($changes[$i]);
267
            }
268
        }
269 View Code Duplication
        foreach ($changes as $change) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
270
            foreach (['begin', 'end'] as $field) {
271
                if (!array_key_exists($change->$field, $slices)) {
272
                    $slices[$change->$field] = (object) [
273
                        'begin'  => $change->$field,
274
                        'end'    => $change->$field,
275
                        'data'   => [],
276
                    ];
277
                }
278
            }
279
        }
280
        ksort($slices);
281
282
        $nextSliceId = null;
283 View Code Duplication
        foreach (array_reverse(array_keys($slices)) as $timestamp) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
284
            if ($nextSliceId) {
285
                $slices[$timestamp]->end = $nextSliceId;
286
            } else {
287
                $slices[$timestamp]->end = 0;
288
            }
289
            $nextSliceId = $timestamp;
290
        }
291
292
        // calculate states
293
        $states = [];
294
        foreach ($slices as $slice) {
295 View Code Duplication
            foreach ($changes as $change) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
296
                if ($change->begin > $slice->begin) {
297
                    // future change
298
                    continue;
299
                }
300
                if ($change->end && ($change->end < $slice->end || !$slice->end)) {
301
                    // complete change
302
                    continue;
303
                }
304
                $callback($slice, $change);
305
            }
306
            if (count((array) $slice->data)) {
307
                $states[] = $slice;
308
            }
309
        }
310
311
        // merge states
312
        $clean = false;
313 View Code Duplication
        while (!$clean) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
314
            $clean = true;
315
            foreach ($states as $i => $state) {
316
                if (array_key_exists($i+1, $states)) {
317
                    $next = $states[$i+1];
318
                    if (json_encode($state->data) == json_encode($next->data)) {
319
                        $state->end = $next->end;
320
                        unset($states[$i+1]);
321
                        $states = array_values($states);
322
                        $clean = false;
323
                        break;
324
                    }
325
                }
326
            }
327
        }
328
329
        return $states;
330
    }
331
}
332