Completed
Push — master ( 45846d...2ad132 )
by Dmitry
02:06
created

Space::addIndex()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 4
rs 10
c 0
b 0
f 0
cc 1
eloc 2
nc 1
nop 1
1
<?php
2
3
namespace Tarantool\Mapper;
4
5
use Exception;
6
7
class Space
8
{
9
    private $mapper;
10
11
    private $id;
12
    private $name;
13
    private $format;
14
    private $indexes;
15
16
    private $formatNamesHash = [];
17
    private $formatTypesHash = [];
18
19
    private $repository;
20
21
    public function __construct(Mapper $mapper, $id, $name)
22
    {
23
        $this->mapper = $mapper;
24
        $this->id = $id;
25
        $this->name = $name;
26
    }
27
28
    public function addProperties($config)
29
    {
30
        foreach($config as $name => $type) {
31
            $this->addProperty($name, $type);
32
        }
33
        return $this;
34
    }
35
36
    public function addProperty($name, $type)
37
    {
38
        $format = $this->getFormat();
39
        foreach($format as $field) {
40
            if($field['name'] == $name) {
41
                throw new Exception("Property $name exists");
42
            }
43
        }
44
        $format[] = compact('name', 'type');
45
        $this->format = $format;
46
        $this->mapper->getClient()->evaluate("box.space[$this->id]:format(...)", [$format]);
47
48
        $this->parseFormat();
49
50
        return $this;
51
    }
52
53
    public function removeProperty($name)
54
    {
55
        $format = $this->getFormat();
56
        $last = array_pop($format);
57
        if($last['name'] != $name) {
58
            throw new Exception("Remove only last property");
59
        }
60
        $this->mapper->getClient()->evaluate("box.space[$this->id]:format(...)", [$format]);
61
        $this->format = $format;
62
63
        $this->parseFormat();
64
65
        return $this;
66
    }
67
68
    public function removeIndex($name)
69
    {
70
        $this->mapper->getClient()->evaluate("box.space[$this->id].index.$name:drop()");
71
        $this->indexes = [];
72
        $this->mapper->getRepository('_index')->flushCache();
73
74
        return $this;
75
    }
76
77
    public function addIndex($config)
78
    {
79
        return $this->createIndex($config);
80
    }
81
82
    public function createIndex($config)
83
    {
84
85
        if(!is_array($config)) {
86
            $config = ['fields' => $config];
87
        }
88
89
90
        if(!array_key_exists('fields', $config)) {
91
            if(array_values($config) != $config) {
92
                throw new Exception("Invalid index configuration");
93
            }
94
            $config = [
95
                'fields' => $config
96
            ];
97
        }
98
99
        if(!is_array($config['fields'])) {
100
            $config['fields'] = [$config['fields']];
101
        }
102
103
        $options = [
104
            'parts' => []
105
        ];
106
107
        foreach($config as $k => $v) {
108
            if($k != 'name' && $k != 'fields') {
109
                $options[$k] = $v;
110
            }
111
        }
112
113
        foreach($config['fields'] as $property) {
114
            if(!$this->getPropertyType($property)) {
115
                throw new Exception("Unknown property $property", 1);
116
            }
117
            $options['parts'][] = $this->getPropertyIndex($property)+1;
118
            $options['parts'][] = $this->getPropertyType($property);
119
        }
120
121
        $name = array_key_exists('name', $config) ? $config['name'] : implode('_', $config['fields']);
122
123
        $this->mapper->getClient()->evaluate("box.space[$this->id]:create_index('$name', ...)", [$options]);
124
        $this->indexes = [];
125
126
        return $this;
127
    }
128
129
    public function isSpecial()
130
    {
131
        return $this->id == 280 || $this->id == 288;
132
    }
133
134
    public function getId()
135
    {
136
        return $this->id;
137
    }
138
139
    public function getFormat()
140
    {
141
        if(!$this->format) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->format of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
142
            if($this->isSpecial()) {
143
                $this->format = $this->mapper->getClient()
144
                    ->getSpace(280)->select([$this->id])->getData()[0][6];
145
146
            } else {
147
                $this->format = $this->mapper->findOne('_space', ['id' => $this->id])->format;
148
            }
149
            if(!$this->format) {
150
                $this->format = [];
151
            }
152
            $this->parseFormat();
153
        }
154
155
        return $this->format;
156
    }
157
158
    public function getMapper()
159
    {
160
        return $this->mapper;
161
    }
162
163
    public function getName()
164
    {
165
        return $this->name;
166
    }
167
168
    private function parseFormat()
169
    {
170
        $this->formatTypesHash = [];
171
        $this->formatNamesHash = [];
172
        foreach($this->format as $key => $row) {
173
            $this->formatTypesHash[$row['name']] = $row['type'];
174
            $this->formatNamesHash[$row['name']] = $key;
175
        }
176
        return $this;
177
    }
178
179
    public function hasProperty($name)
180
    {
181
        $this->getFormat();
182
        return array_key_exists($name, $this->formatNamesHash);
183
    }
184
185
    public function getPropertyType($name)
186
    {
187
        if(!$this->hasProperty($name)) {
188
            throw new Exception("No property $name");
189
        }
190
        return $this->formatTypesHash[$name];
191
    }
192
193
    public function getPropertyIndex($name)
194
    {
195
        if(!$this->hasProperty($name)) {
196
            throw new Exception("No property $name");
197
        }
198
        return $this->formatNamesHash[$name];
199
    }
200
201
    public function getIndexes()
202
    {
203
        if(!$this->indexes) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->indexes of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
204
            if($this->isSpecial()) {
205
                $this->indexes = [];
206
                $indexTuples = $this->mapper->getClient()->getSpace(288)->select([$this->id])->getData();
207
                $indexFormat = $this->mapper->getSchema()->getSpace(288)->getFormat();
208
                foreach($indexTuples as $tuple) {
209
                    $instance = (object) [];
210
                    foreach($indexFormat as $index => $format) {
211
                        $instance->{$format['name']} = $tuple[$index];
212
                    }
213
                    $this->indexes[] = $instance;
214
                }
215
216
            } else {
217
                $this->indexes = $this->mapper->find('_index', ['id' => $this->id]);
218
            }
219
        }
220
        return $this->indexes;
221
    }
222
223
    public function castIndex($params)
224
    {
225
        $keys = array_keys($params);
0 ignored issues
show
Unused Code introduced by
$keys is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
226
227
        $keys = [];
228
        foreach($params as $name => $value) {
229
            $keys[] = $this->getPropertyIndex($name);
230
        }
231
        if($keys == [0]) {
232
            // primary index
233
            return 0;
234
        }
235
236
        // equals
237
        foreach($this->getIndexes() as $index) {
238
            $equals = false;
239
            if(count($keys) == count($index->parts)) {
240
                // same length
241
                $equals = true;
242
                foreach($index->parts as $part) {
243
                    $equals = $equals && in_array($part[0], $keys);
244
                }
245
            }
246
247
            if($equals) {
248
                return $index->iid;
249
            }
250
        }
251
252
        // index part
253
        foreach($this->getIndexes() as $index) {
254
            $partial = [];
255
            foreach($index->parts as $n => $part) {
256
                if(!array_key_exists($n, $keys)) {
257
                    break;
258
                }
259
                if($keys[$n] != $part[0]) {
260
                    break;
261
                }
262
                $partial[] = $keys[$n];
263
            }
264
265
            if(count($partial) == count($keys)) {
266
                return $index->iid;
267
            }
268
        }
269
270
        throw new Exception("No index");
271
    }
272
273
    public function getIndexValues($indexId, $params)
274
    {
275
        $index = $this->getIndexes()[$indexId];
276
        $format = $this->getFormat();
277
278
        $values = [];
279
        foreach($index->parts as $part) {
280
            $name = $format[$part[0]]['name'];
281
            if(!array_key_exists($name, $params)) {
282
                break;
283
            }
284
            $values[] = $this->mapper->getSchema()->formatValue($part[1], $params[$name]);
285
        }
286
        return $values;
287
    }
288
289
    public function getPrimaryIndex()
290
    {
291
        $indexes = $this->getIndexes();
292
        if(!count($indexes)) {
293
            throw new Exception("No primary index");
294
        }
295
        return $indexes[0];
296
    }
297
298
    public function getTupleKey($tuple)
299
    {
300
        $key = [];
301
        foreach($this->getPrimaryIndex()->parts as $part) {
302
            $key[] = $tuple[$part[0]];
303
        }
304
        return count($key) == 1 ? $key[0] : implode(':', $key);
305
    }
306
307
    public function getInstanceKey($instance)
308
    {
309
310
        $key = [];
311
312
        foreach($this->getPrimaryIndex()->parts as $part) {
313
            $name = $this->getFormat()[$part[0]]['name'];
314
            if(!property_exists($instance, $name)) {
315
                throw new Exception("Field $name is undefined", 1);
316
            }
317
            $key[] = $instance->$name;
318
        }
319
320
        return count($key) == 1 ? $key[0] : implode(':', $key);
321
    }
322
323
    public function getRepository()
324
    {
325
        $class = Repository::class;
326
        foreach($this->mapper->getPlugins() as $plugin) {
327
            $repositoryClass = $plugin->getRepositoryClass($this);
328
            if($repositoryClass) {
329
                if($class != Repository::class) {
330
                    throw new Exception('Repository class override');
331
                }
332
                $class = $repositoryClass;
333
            }
334
        }
335
        return $this->repository ?: $this->repository = new $class($this);
336
    }
337
}
338