Completed
Push — master ( f50da1...b30b9b )
by Dmitry
02:24
created

NestedSet::addIndexes()   A

Complexity

Conditions 4
Paths 5

Size

Total Lines 19
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Importance

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