Completed
Push — master ( 1555ad...e3c0b5 )
by Dmitry
02:27
created

Aggregator::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 4
rs 10
cc 1
eloc 2
nc 1
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
    private $createReferenceAggregate = true;
12
13
    public function __construct(Temporal $temporal)
14
    {
15
        $this->temporal = $temporal;
16
    }
17
18
    public function setReferenceAggregation($value)
19
    {
20
        $this->createReferenceAggregate = $value;
21
    }
22
23
    public function getLeafs($link)
24
    {
25
        if ($link->timestamp) {
26
            return [$link];
27
        }
28
29
        $leafs = [];
30
        foreach ($this->temporal->getMapper()->find('_temporal_link', ['parent' => $link->id]) as $child) {
31
            foreach ($this->getLeafs($child) as $leaf) {
32
                $leafs[] = $leaf;
33
            }
34
        }
35
        return $leafs;
36
    }
37
38
    public function updateReferenceState($entity, $id, $target)
39
    {
40
        $mapper = $this->temporal->getMapper();
41
42
        $params = [
43
            'entity' => $this->temporal->entityNameToId($entity),
44
            'id'     => $id,
45
            'target' => $this->temporal->entityNameToId($target),
46
        ];
47
48
        $changes = $mapper->find('_temporal_reference', $params);
49
        $states = $this->generateStates($changes, function ($state, $change) {
50
            $state->data = $change->targetId;
51
        });
52
53
        $affected = [];
54
        foreach ($mapper->find('_temporal_reference_state', $params) as $state) {
55
            $mapper->remove($state);
56
        }
57
58
        foreach ($states as $state) {
59
            $entity = $mapper->create('_temporal_reference_state', array_merge($params, [
60
                'begin' => $state->begin,
61
                'end' => $state->end,
62
                'targetId' => $state->data,
63
            ]));
64
            if (!in_array([$entity->target, $entity->targetId], $affected)) {
65
                $affected[] = [$entity->target, $entity->targetId];
66
            }
67
        }
68
69
        if (!$this->createReferenceAggregate) {
70
            return $affected;
71
        }
72
73
        foreach ($affected as $affect) {
74
            list($entity, $entityId) = $affect;
75
            $changes = $mapper->find('_temporal_reference_state', [
76
                'target' => $entity,
77
                'targetId' => $entityId,
78
                'entity' => $params['entity'],
79
            ]);
80
            $aggregates = $this->generateStates($changes, function ($state, $change) {
81
                if (!in_array($change->id, $state->data)) {
82
                    $state->data[] = $change->id;
83
                }
84
                $state->exists = false;
85
            });
86
87
            $aggregateParams = [
88
                'entity' => $entity,
89
                'id' => $entityId,
90
                'source' => $params['entity']
91
            ];
92 View Code Duplication
            foreach ($mapper->find('_temporal_reference_aggregate', $aggregateParams) as $aggregate) {
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...
93
                foreach ($aggregates as $candidate) {
94
                    if ($candidate->begin == $aggregate->begin && $candidate->end == $aggregate->end && $candidate->data == $aggregate->data) {
95
                        $candidate->exists = true;
96
                        continue 2;
97
                    }
98
                }
99
                $mapper->remove($aggregate);
100
            }
101 View Code Duplication
            foreach ($aggregates as $aggregate) {
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...
102
                if ($aggregate->exists) {
103
                    continue;
104
                }
105
                $mapper->create('_temporal_reference_aggregate', array_merge($aggregateParams, [
106
                    'begin' => $aggregate->begin,
107
                    'end' => $aggregate->end,
108
                    'data' => $aggregate->data,
109
                ]));
110
            }
111
        }
112
    }
113
114
115
    public function updateLinkAggregation(Entity $node)
116
    {
117
        $todo = [
118
            $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...
119
        ];
120
121
        $current = $node;
122
        while ($current->parent) {
123
            $current = $this->temporal->getMapper()->findOne('_temporal_link', ['id' => $current->parent]);
124
            $todo[$this->temporal->entityIdToName($current->entity)] = $current->entityId;
125
        }
126
127
        foreach ($todo as $entity => $id) {
128
            $spaceId = $this->temporal->entityNameToId($entity);
129
            $source = $this->temporal->getMapper()->find('_temporal_link', [
130
                'entity'   => $spaceId,
131
                'entityId' => $id,
132
            ]);
133
134
            $leafs = [];
135
            foreach ($source as $node) {
136
                foreach ($this->getLeafs($node) as $detail) {
137
                    $leafs[] = $detail;
138
                }
139
            }
140
141
            $changeaxis = [];
142
143
            foreach ($leafs as $leaf) {
144
                $current = $leaf;
145
                $ref = [];
146
147
                if (property_exists($leaf, 'idle') && $leaf->idle) {
148
                    continue;
149
                }
150
151
                while ($current) {
152
                    if ($current->entity != $spaceId) {
153
                        $ref[$current->entity] = $current->entityId;
154
                    }
155
                    if ($current->parent) {
156
                        $current = $this->temporal->getMapper()->findOne('_temporal_link', $current->parent);
157
                    } else {
158
                        $current = null;
159
                    }
160
                }
161
162
                $data = [$ref];
163
                if (property_exists($leaf, 'data') && $leaf->data) {
164
                    $data[] = $leaf->data;
165
                }
166
167
                if (!array_key_exists($leaf->timestamp, $changeaxis)) {
168
                    $changeaxis[$leaf->timestamp] = [];
169
                }
170
                $changeaxis[$leaf->timestamp][] = (object) [
171
                    'begin' => $leaf->begin,
172
                    'end' => $leaf->end,
173
                    'data' => $data
174
                ];
175
            }
176
177
            $params = [
178
                'entity' => $spaceId,
179
                'id'     => $id,
180
            ];
181
182
            $timeaxis = [];
183
            foreach ($changeaxis as $timestamp => $changes) {
184 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...
185
                    foreach (['begin', 'end'] as $field) {
186
                        if (!array_key_exists($change->$field, $timeaxis)) {
187
                            $timeaxis[$change->$field] = (object) [
188
                                'begin' => $change->$field,
189
                                'end'   => $change->$field,
190
                                'data'  => [],
191
                            ];
192
                        }
193
                    }
194
                }
195
            }
196
197
            ksort($changeaxis);
198
            ksort($timeaxis);
199
200
            $nextSliceId = null;
201 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...
202
                if ($nextSliceId) {
203
                    $timeaxis[$timestamp]->end = $nextSliceId;
204
                } else {
205
                    $timeaxis[$timestamp]->end = 0;
206
                }
207
                $nextSliceId = $timestamp;
208
            }
209
210
            $states = [];
211
            foreach ($timeaxis as $state) {
212 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...
213
                    foreach ($changes as $change) {
214
                        if ($change->begin > $state->begin) {
215
                            // future override
216
                            continue;
217
                        }
218
                        if ($change->end && ($change->end < $state->end || !$state->end)) {
219
                            // complete override
220
                            continue;
221
                        }
222
                        $state->data[] = $change->data;
223
                    }
224
                }
225
                if (count($state->data)) {
226
                    $states[] = (object) array_merge(get_object_vars($state), $params);
227
                }
228
            }
229
230
            // merge states
231
            $clean = false;
232 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...
233
                $clean = true;
234
                foreach ($states as $i => $state) {
235
                    if (array_key_exists($i+1, $states)) {
236
                        $next = $states[$i+1];
237
                        if (json_encode($state->data) == json_encode($next->data)) {
238
                            $states[$i]->end = $next->end;
239
                            unset($states[$i+1]);
240
                            $states = array_values($states);
241
                            $clean = false;
242
                            break;
243
                        }
244
                    }
245
                }
246
            }
247
248
            foreach ($this->temporal->getMapper()->find('_temporal_link_aggregate', $params) as $state) {
249
                $this->temporal->getMapper()->remove($state);
250
            }
251
252
            foreach ($states as $state) {
253
                $this->temporal->getMapper()->create('_temporal_link_aggregate', $state);
254
            }
255
        }
256
    }
257
258
    public function updateOverrideAggregation($entity, $id)
259
    {
260
        $mapper = $this->temporal->getMapper();
261
        $params = [
262
            'entity' => $this->temporal->entityNameToId($entity),
263
            'id'     => $id,
264
        ];
265
266
        $changes = $mapper->find('_temporal_override', $params);
267
        $states = $this->generateStates($changes, function ($state, $change) {
268
            $state->data = array_merge($state->data, $change->data);
269
            $state->exists = false;
270
        });
271 View Code Duplication
        foreach ($mapper->find('_temporal_override_aggregate', $params) as $aggregate) {
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...
272
            foreach ($states as $state) {
273
                if ($state->begin == $aggregate->begin && $state->end == $aggregate->end && $state->data == $aggregate->data) {
274
                    $state->exists = true;
275
                    continue 2;
276
                }
277
            }
278
            $mapper->remove($aggregate);
279
        }
280 View Code Duplication
        foreach ($states as $aggregate) {
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...
281
            if ($aggregate->exists) {
282
                continue;
283
            }
284
            $mapper->create('_temporal_override_aggregate', array_merge($params, [
285
                'begin' => $aggregate->begin,
286
                'end' => $aggregate->end,
287
                'data' => $aggregate->data,
288
            ]));
289
        }
290
    }
291
292
    private function generateStates($changes, $callback)
293
    {
294
        $slices = [];
295
        foreach ($changes as $i => $change) {
296
            if (property_exists($change, 'idle') && $change->idle) {
297
                unset($changes[$i]);
298
            }
299
        }
300 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...
301
            foreach (['begin', 'end'] as $field) {
302
                if (!array_key_exists($change->$field, $slices)) {
303
                    $slices[$change->$field] = (object) [
304
                        'begin'  => $change->$field,
305
                        'end'    => $change->$field,
306
                        'data'   => [],
307
                    ];
308
                }
309
            }
310
        }
311
        ksort($slices);
312
313
        $nextSliceId = null;
314 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...
315
            if ($nextSliceId) {
316
                $slices[$timestamp]->end = $nextSliceId;
317
            } else {
318
                $slices[$timestamp]->end = 0;
319
            }
320
            $nextSliceId = $timestamp;
321
        }
322
323
        // calculate states
324
        $states = [];
325
        foreach ($slices as $slice) {
326 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...
327
                if ($change->begin > $slice->begin) {
328
                    // future change
329
                    continue;
330
                }
331
                if ($change->end && ($change->end < $slice->end || !$slice->end)) {
332
                    // complete change
333
                    continue;
334
                }
335
                $callback($slice, $change);
336
            }
337
            if (count((array) $slice->data)) {
338
                $states[] = $slice;
339
            }
340
        }
341
342
        // merge states
343
        $clean = false;
344 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...
345
            $clean = true;
346
            foreach ($states as $i => $state) {
347
                if (array_key_exists($i+1, $states)) {
348
                    $next = $states[$i+1];
349
                    if (json_encode($state->data) == json_encode($next->data)) {
350
                        $state->end = $next->end;
351
                        unset($states[$i+1]);
352
                        $states = array_values($states);
353
                        $clean = false;
354
                        break;
355
                    }
356
                }
357
            }
358
        }
359
360
        return $states;
361
    }
362
}
363