Completed
Push — master ( b30b9b...187882 )
by Dmitry
04:07 queued 01:47
created

NestedSet   A

Complexity

Total Complexity 22

Size/Duplication

Total Lines 161
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 5

Importance

Changes 0
Metric Value
wmc 22
lcom 1
cbo 5
dl 0
loc 161
rs 10
c 0
b 0
f 0

6 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 4 1
A addIndexes() 0 19 4
B beforeCreate() 0 56 9
A beforeRemove() 0 53 3
A isNested() 0 14 4
A resetNestedSpacesCache() 0 4 1
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', 'root', '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
            ['root', 'left'],
29
            ['root', '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 beforeCreate(Entity $entity, Space $space)
41
    {
42
        if ($this->isNested($space)) {
43
            $repository = $space->getRepository();
44
45
            if ($entity->parent) {
46
                $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...
47
                $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...
48
49
                $updateLeft = [];
50
                $updateRight = [];
51
                foreach ($repository->find(['root' => $entity->root]) as $node) {
0 ignored issues
show
Bug introduced by
The property root 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...
52
                    if ($node->right >= $parent->right) {
53
                        if ($node->left > $parent->right) {
54
                            $updateLeft[$node->left] = $node;
55
                        }
56
                        $updateRight[$node->right] = $node;
57
                    }
58
                }
59
60
                $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...
61
                $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...
62
63
                krsort($updateRight);
64
                foreach ($updateRight as $node) {
65
                    $node->right += 2;
66
                    $node->save();
67
                }
68
69
                krsort($updateLeft);
70
                foreach ($updateLeft as $node) {
71
                    $node->left += 2;
72
                    $node->save();
73
                }
74
            } else {
75
                // new root
76
                $map = $space->getTupleMap();
77
78
                $entity->root = $entity->root ?: 0;
79
                $max = $this->mapper->getClient()->evaluate("
80
                    local max = 0
81
                    local root = $entity->root
82
                    for i, n in box.space.tree.index.root_right:pairs(root, {iterator = 'le'}) do
83
                        if n[$map->root] == root then
84
                            max = n[$map->right]
85
                        end
86
                        break
87
                    end
88
                    return max
89
                ")->getData()[0];
90
91
                $entity->left = $max + 1;
92
                $entity->right = $entity->left + 1;
93
            }
94
        }
95
    }
96
97
    public function beforeRemove(Entity $instance, Space $space)
98
    {
99
        $spaceName = $space->getName();
100
        $map = $space->getTupleMap();
101
102
        $result = $this->mapper->getClient()->evaluate("
103
            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...
104
            local remove_list = {}
105
            local update_list = {}
106
            for i, current in box.space.$spaceName.index.root_left:pairs({removed_node[$map->root], removed_node[$map->left]}, 'gt') do
107
                if current[$map->root] ~= removed_node[$map->root] then
108
                    break
109
                end
110
                if current[$map->left] < removed_node[$map->right] then
111
                    table.insert(remove_list, current[$map->id])
112
                else
113
                    table.insert(update_list, current[$map->id])
114
                end
115
            end
116
117
            local delta = removed_node[$map->right] - removed_node[$map->left] + 1
118
119
            for i, id in ipairs(remove_list) do
120
                box.space.$spaceName:delete(id)
121
            end
122
123
            box.space.$spaceName:update($instance->id, {
124
                {'=', $map->left, 0},
125
                {'=', $map->right, 0},
126
            })
127
128
            for i, id in pairs(update_list) do
129
                box.space.$spaceName:update(id, {
130
                    {'-', $map->left, delta},
131
                    {'-', $map->right, delta}
132
                })
133
            end
134
135
            return remove_list, update_list, delta, removed_node
136
        ")->getData();
137
138
        // remove
139
        foreach ($result[0] as $id) {
140
            $space->getRepository()->forget($id);
141
        }
142
143
        // update
144
        foreach ($result[1] as $id) {
145
            $space->getRepository()->sync($id);
146
        }
147
148
        $space->getRepository()->flushCache();
149
    }
150
151
    public function isNested(Space $space, $force = false)
152
    {
153
        $spaceName = $space->getName();
154
        if ($force || !array_key_exists($spaceName, $this->nestedSpaces)) {
155
            $fields = [];
156
            foreach ($space->getFormat() as $field) {
157
                $fields[] = $field['name'];
158
            }
159
160
            $this->nestedSpaces[$spaceName] = !count(array_diff($this->keys, $fields));
161
        }
162
163
        return $this->nestedSpaces[$spaceName];
164
    }
165
166
    public function resetNestedSpacesCache()
167
    {
168
        $this->nestedSpaces = [];
169
    }
170
}
171