Completed
Push — master ( e4aaff...0786fb )
by Dmitry
03:39
created

NestedSet::beforeUpdate()   D

Complexity

Conditions 10
Paths 37

Size

Total Lines 123
Code Lines 68

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 123
rs 4.8196
c 0
b 0
f 0
cc 10
eloc 68
nc 37
nop 2

How to fix   Long Method    Complexity   

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;
4
5
use Tarantool\Mapper\Entity;
6
use Tarantool\Mapper\Mapper;
7
use Tarantool\Mapper\Plugin;
8
use Tarantool\Mapper\Space;
9
10
class NestedSet extends Plugin
11
{
12
    private $keys = ['id', 'parent', 'group', 'depth', 'left', 'right'];
13
    private $nestedSpaces = [];
14
15
    public function __construct(Mapper $mapper)
16
    {
17
        $this->mapper = $mapper;
18
    }
19
20
    public function addIndexes(Space $space)
21
    {
22
        $indexes = [
23
            ['id'],
24
            [
25
                'fields' => ['parent'],
26
                'unique' => false,
27
            ],
28
            ['group', 'left'],
29
            ['group', 'right'],
30
        ];
31
32
        foreach ($indexes as $index) {
33
            $fields = array_key_exists('fields', $index) ? $index['fields'] : $index;
34
            if ($space->castIndex(array_flip($fields), true) === null) {
35
                $space->createIndex($index);
36
            }
37
        }
38
    }
39
40
    public function beforeUpdate(Entity $entity, Space $space)
41
    {
42
        if ($this->isNested($space)) {
43
            $repository = $space->getRepository();
44
45
            $parent = $repository->findOne($entity->parent);
0 ignored issues
show
Bug introduced by
The property parent 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...
46
            $level_up = $parent ? $parent->depth+1 : 0;
47
48
            $left_key = $entity->left;
0 ignored issues
show
Bug introduced by
The property left 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...
49
            $right_key = $entity->right;
0 ignored issues
show
Bug introduced by
The property right 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...
50
51
            $client = $this->mapper->getClient();
52
            $map = $space->getTupleMap();
53
54
            $old_entity = $client->getSpace($space->getId())->select([$entity->id])->getData()[0];
0 ignored issues
show
Bug introduced by
The property id 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...
55
            $old_parent = $repository->findOne($old_entity[$map->parent-1]);
56
            $old_parent_id = $old_parent ? $old_parent->id : 0;
57
58
            if ($old_parent_id != $entity->parent) {
59
                $right_key_near = 0;
60
                if (!$entity->parent) {
61
                    foreach ($repository->find(['parent' => 0]) as $node) {
62
                        if ($node->right > $right_key_near) {
63
                            $right_key_near = $node->right;
64
                        }
65
                    }
66
                } else {
67
                    $right_key_near = $parent->left;
68
                }
69
70
                $skew_tree = $right_key - $left_key + 1;
71
                $skew_edit = $right_key_near - $left_key + 1;
72
                $skew_level = $level_up - $entity->depth;
0 ignored issues
show
Bug introduced by
The property depth 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...
73
74
                $spaceName = $space->getName();
75
76
                if ($right_key < $right_key_near) {
77
                    $skew_edit -= $skew_tree;
78
                }
79
                $result = $this->mapper->getClient()->evaluate("
80
                    local result = {}
81
                    local updates = {}
82
                    local leftKeys = {}
83
                    local rightKeys = {}
84
                    local maxRightTuple = box.space.$spaceName.index.group_right:max(right);
85
                    local maxLeftTuple = box.space.$spaceName.index.group_left:max(left);
86
                    local maxRight = 100
87
                    if maxRightTuple ~= nil then
88
                        maxRight = maxRightTuple[$map->right]+100
89
                    end
90
                    local maxLeft = 100
91
                    if maxLeftTuple ~= nil then
92
                        maxLeft = maxLeftTuple[$map->left]+100
93
                    end
94
                    box.begin()
95
                    for i, node in box.space.$spaceName:pairs() do
96
                        if $right_key < $right_key_near then
97
                            if node[$map->right] > $left_key and node[$map->left] <= $right_key_near then
98
                                if node[$map->right] <= $right_key then
99
                                    table.insert(updates, {node[$map->id], $map->left, node[$map->left]+$skew_edit})
100
                                    box.space.$spaceName:update(node[$map->id], {{'=', $map->left, maxLeft}})
101
                                    maxLeft = maxLeft+1
102
                                elseif node[$map->left] > $right_key then
103
                                    table.insert(updates, {node[$map->id], $map->left, node[$map->left]-$skew_tree})
104
                                    box.space.$spaceName:update(node[$map->id], {{'=', $map->left, maxLeft}})
105
                                    maxLeft = maxLeft+1
106
                                end
107
                                if node[$map->right] <= $right_key then
108
                                    table.insert(updates, {node[$map->id], $map->depth, node[$map->depth]+$skew_level})
109
                                end
110
                                if node[$map->right] <= $right_key then
111
                                    table.insert(updates, {node[$map->id], $map->right, node[$map->right]+$skew_edit})
112
                                    box.space.$spaceName:update(node[$map->id], {{'=', $map->right, maxRight}})
113
                                    maxRight = maxRight+1
114
                                elseif node[$map->right] <= $right_key_near then
115
                                    table.insert(updates, {node[$map->id], $map->right, node[$map->right]-$skew_tree})
116
                                    box.space.$spaceName:update(node[$map->id], {{'=', $map->right, maxRight}})
117
                                    maxRight = maxRight+1
118
                                end
119
                            end
120
                        else
121
                            if node[$map->right] > $right_key_near and node[$map->left] < $right_key then
122
                                if node[$map->left] >= $left_key then
123
                                    table.insert(updates, {node[$map->id], $map->right, node[$map->right]+$skew_edit})
124
                                    box.space.$spaceName:update(node[$map->id], {{'=', $map->right, maxRight}})
125
                                    maxRight = maxRight+1
126
                                elseif node[$map->right] < $left_key then
127
                                    table.insert(updates, {node[$map->id], $map->right, node[$map->right]+$skew_tree})
128
                                    box.space.$spaceName:update(node[$map->id], {{'=', $map->right, maxRight}})
129
                                    maxRight = maxRight+1
130
                                end
131
                                if node[$map->left] >= $left_key then
132
                                    table.insert(updates, {node[$map->id], $map->depth, node[$map->depth]+$skew_level})
133
                                end
134
                                if node[$map->left] >= $left_key then
135
                                    table.insert(updates, {node[$map->id], $map->left, node[$map->left]+$skew_edit})
136
                                    box.space.$spaceName:update(node[$map->id], {{'=', $map->left, maxLeft}})
137
                                    maxLeft = maxLeft+1
138
                                elseif node[$map->left] > $right_key_near then
139
                                    table.insert(updates, {node[$map->id], $map->left, node[$map->left]+$skew_tree})
140
                                    box.space.$spaceName:update(node[$map->id], {{'=', $map->left, maxLeft}})
141
                                    maxLeft = maxLeft+1
142
                                end
143
                            end
144
                        end
145
                    end
146
                    for i, node in pairs(updates) do
147
                        table.insert(result, node[1])
148
                        box.space.$spaceName:update(node[1], {{'=', node[2], node[3]}})
149
                    end
150
                    box.commit()
151
152
                    return result
153
                ")->getData();
154
155
                foreach (array_unique($result[0]) as $id) {
156
                    $space->getRepository()->sync($id);
157
                }
158
159
                $space->getRepository()->flushCache();
160
            }
161
        }
162
    }
163
164
    public function beforeCreate(Entity $entity, Space $space)
165
    {
166
        if (!$this->isNested($space)) {
167
            return;
168
        }
169
        $repository = $space->getRepository();
170
171
        if ($entity->parent) {
172
            $parent = $repository->findOne($entity->parent);
0 ignored issues
show
Bug introduced by
The property parent 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...
173
            $entity->depth = $parent->depth + 1;
0 ignored issues
show
Bug introduced by
The property depth 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...
174
175
            $updateLeft = [];
176
            $updateRight = [];
177
            foreach ($repository->find(['group' => $entity->group]) as $node) {
0 ignored issues
show
Bug introduced by
The property group 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...
178
                if ($node->right >= $parent->right) {
179
                    if ($node->left > $parent->right) {
180
                        $updateLeft[$node->left] = $node;
181
                    }
182
                    $updateRight[$node->right] = $node;
183
                }
184
            }
185
186
            $entity->left = $parent->right;
0 ignored issues
show
Bug introduced by
The property left 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...
187
            $entity->right = $entity->left + 1;
0 ignored issues
show
Bug introduced by
The property right 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...
188
189
            krsort($updateRight);
190
            foreach ($updateRight as $node) {
191
                $node->right += 2;
192
                $node->save();
193
            }
194
195
            krsort($updateLeft);
196
            foreach ($updateLeft as $node) {
197
                $node->left += 2;
198
                $node->save();
199
            }
200
        } else {
201
            // new group
202
            $map = $space->getTupleMap();
203
            $spaceName = $space->getName();
204
205
            $entity->group = $entity->group ?: 0;
206
            $max = $this->mapper->getClient()->evaluate("
207
                local max = 0
208
                local group = $entity->group
209
                for i, n in box.space.$spaceName.index.group_right:pairs(group, {iterator = 'le'}) do
210
                    if n[$map->group] == group then
211
                        max = n[$map->right]
212
                    end
213
                    break
214
                end
215
                return max
216
            ")->getData()[0];
217
218
            $entity->left = $max + 1;
219
            $entity->right = $entity->left + 1;
220
        }
221
    }
222
223
    public function beforeRemove(Entity $instance, Space $space)
224
    {
225
        if (!$this->isNested($space)) {
226
            return;
227
        }
228
229
        $spaceName = $space->getName();
230
        $map = $space->getTupleMap();
231
232
        $result = $this->mapper->getClient()->evaluate("
233
            local removed_node = box.space.$spaceName:get($instance->id)
0 ignored issues
show
Bug introduced by
The property id 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...
234
            local remove_list = {}
235
            local update_list = {}
236
            for i, current in box.space.$spaceName.index.group_left:pairs({removed_node[$map->group], removed_node[$map->left]}, 'gt') do
237
                if current[$map->group] ~= removed_node[$map->group] then
238
                    break
239
                end
240
                if current[$map->left] < removed_node[$map->right] then
241
                    table.insert(remove_list, current[$map->id])
242
                else
243
                    table.insert(update_list, current[$map->id])
244
                end
245
            end
246
247
            local delta = removed_node[$map->right] - removed_node[$map->left] + 1
248
249
            for i, id in ipairs(remove_list) do
250
                box.space.$spaceName:delete(id)
251
            end
252
253
            box.space.$spaceName:update($instance->id, {
254
                {'=', $map->left, 0},
255
                {'=', $map->right, 0},
256
            })
257
258
            for i, id in pairs(update_list) do
259
                box.space.$spaceName:update(id, {
260
                    {'-', $map->left, delta},
261
                    {'-', $map->right, delta}
262
                })
263
            end
264
265
            return remove_list, update_list, delta, removed_node
266
        ")->getData();
267
268
        // remove
269
        foreach ($result[0] as $id) {
270
            $space->getRepository()->forget($id);
271
        }
272
273
        // update
274
        foreach ($result[1] as $id) {
275
            $space->getRepository()->sync($id);
276
        }
277
278
        $space->getRepository()->flushCache();
279
    }
280
281
    public function isNested(Space $space, $force = false)
282
    {
283
        $spaceName = $space->getName();
284
        if ($force || !array_key_exists($spaceName, $this->nestedSpaces)) {
285
            $fields = [];
286
            foreach ($space->getFormat() as $field) {
287
                $fields[] = $field['name'];
288
            }
289
290
            $this->nestedSpaces[$spaceName] = !count(array_diff($this->keys, $fields));
291
        }
292
293
        return $this->nestedSpaces[$spaceName];
294
    }
295
296
    public function resetNestedSpacesCache()
297
    {
298
        $this->nestedSpaces = [];
299
    }
300
}
301