Completed
Push — master ( 460453...11031e )
by Dmitry
03:14
created

Space::getIndexValues()   B

Complexity

Conditions 6
Paths 12

Size

Total Lines 24
Code Lines 16

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 24
rs 8.5125
c 0
b 0
f 0
cc 6
eloc 16
nc 12
nop 2
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
        $this->mapper->getSchema()->getSpace('_index')->getRepository()->flushCache();
127
128
        return $this;
129
    }
130
131
    public function isSpecial()
132
    {
133
        return $this->id == 280 || $this->id == 288;
134
    }
135
136
    public function getId()
137
    {
138
        return $this->id;
139
    }
140
141
    public function getFormat()
142
    {
143
        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...
144
            if($this->isSpecial()) {
145
                $this->format = $this->mapper->getClient()
146
                    ->getSpace(280)->select([$this->id])->getData()[0][6];
147
148
            } else {
149
                $this->format = $this->mapper->findOne('_space', ['id' => $this->id])->format;
150
            }
151
            if(!$this->format) {
152
                $this->format = [];
153
            }
154
            $this->parseFormat();
155
        }
156
157
        return $this->format;
158
    }
159
160
    public function getMapper()
161
    {
162
        return $this->mapper;
163
    }
164
165
    public function getName()
166
    {
167
        return $this->name;
168
    }
169
170
    private function parseFormat()
171
    {
172
        $this->formatTypesHash = [];
173
        $this->formatNamesHash = [];
174
        foreach($this->format as $key => $row) {
175
            $this->formatTypesHash[$row['name']] = $row['type'];
176
            $this->formatNamesHash[$row['name']] = $key;
177
        }
178
        return $this;
179
    }
180
181
    public function hasProperty($name)
182
    {
183
        $this->getFormat();
184
        return array_key_exists($name, $this->formatNamesHash);
185
    }
186
187
    public function getPropertyType($name)
188
    {
189
        if(!$this->hasProperty($name)) {
190
            throw new Exception("No property $name");
191
        }
192
        return $this->formatTypesHash[$name];
193
    }
194
195
    public function getPropertyIndex($name)
196
    {
197
        if(!$this->hasProperty($name)) {
198
            throw new Exception("No property $name");
199
        }
200
        return $this->formatNamesHash[$name];
201
    }
202
203
    public function getIndexes()
204
    {
205
        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...
206
            if($this->isSpecial()) {
207
                $this->indexes = [];
208
                $indexTuples = $this->mapper->getClient()->getSpace(288)->select([$this->id])->getData();
209
                $indexFormat = $this->mapper->getSchema()->getSpace(288)->getFormat();
210
                foreach($indexTuples as $tuple) {
211
                    $instance = (object) [];
212
                    foreach($indexFormat as $index => $format) {
213
                        $instance->{$format['name']} = $tuple[$index];
214
                    }
215
                    $this->indexes[] = $instance;
216
                }
217
218
            } else {
219
                $this->indexes = $this->mapper->find('_index', ['id' => $this->id]);
220
            }
221
        }
222
        return $this->indexes;
223
    }
224
225
    public function castIndex($params)
226
    {
227
        $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...
228
229
        $keys = [];
230
        foreach($params as $name => $value) {
231
            $keys[] = $this->getPropertyIndex($name);
232
        }
233
        if($keys == [0]) {
234
            // primary index
235
            return 0;
236
        }
237
238
        // equals
239
        foreach($this->getIndexes() as $index) {
240
            $equals = false;
241
            if(count($keys) == count($index->parts)) {
242
                // same length
243
                $equals = true;
244
                foreach($index->parts as $part) {
245
                    $equals = $equals && in_array($part[0], $keys);
246
                }
247
            }
248
249
            if($equals) {
250
                return $index->iid;
251
            }
252
        }
253
254
        // index part
255
        foreach($this->getIndexes() as $index) {
256
            $partial = [];
257
            foreach($index->parts as $n => $part) {
258
                if(!array_key_exists($n, $keys)) {
259
                    break;
260
                }
261
                if($keys[$n] != $part[0]) {
262
                    break;
263
                }
264
                $partial[] = $keys[$n];
265
            }
266
267
            if(count($partial) == count($keys)) {
268
                return $index->iid;
269
            }
270
        }
271
272
        throw new Exception("No index");
273
    }
274
275
    public function getIndexValues($indexId, $params)
276
    {
277
        $index = null;
278
        foreach($this->getIndexes() as $candidate) {
279
            if($candidate->iid == $indexId) {
280
                $index = $candidate;
281
                break;
282
            }
283
        }
284
        if(!$index) {
285
            throw new Exception("Undefined index: $indexId");
286
        }
287
288
        $format = $this->getFormat();
289
        $values = [];
290
        foreach($index->parts as $part) {
291
            $name = $format[$part[0]]['name'];
292
            if(!array_key_exists($name, $params)) {
293
                break;
294
            }
295
            $values[] = $this->mapper->getSchema()->formatValue($part[1], $params[$name]);
296
        }
297
        return $values;
298
    }
299
300
    public function getPrimaryIndex()
301
    {
302
        $indexes = $this->getIndexes();
303
        if(!count($indexes)) {
304
            throw new Exception("No primary index");
305
        }
306
        return $indexes[0];
307
    }
308
309
    public function getTupleKey($tuple)
310
    {
311
        $key = [];
312
        foreach($this->getPrimaryIndex()->parts as $part) {
313
            $key[] = $tuple[$part[0]];
314
        }
315
        return count($key) == 1 ? $key[0] : implode(':', $key);
316
    }
317
318
    public function getInstanceKey($instance)
319
    {
320
321
        $key = [];
322
323
        foreach($this->getPrimaryIndex()->parts as $part) {
324
            $name = $this->getFormat()[$part[0]]['name'];
325
            if(!property_exists($instance, $name)) {
326
                throw new Exception("Field $name is undefined", 1);
327
            }
328
            $key[] = $instance->$name;
329
        }
330
331
        return count($key) == 1 ? $key[0] : implode(':', $key);
332
    }
333
334
    public function getRepository()
335
    {
336
        $class = Repository::class;
337
        foreach($this->mapper->getPlugins() as $plugin) {
338
            $repositoryClass = $plugin->getRepositoryClass($this);
339
            if($repositoryClass) {
340
                if($class != Repository::class) {
341
                    throw new Exception('Repository class override');
342
                }
343
                $class = $repositoryClass;
344
            }
345
        }
346
        return $this->repository ?: $this->repository = new $class($this);
347
    }
348
349
    public function repositoryExists()
350
    {
351
        return !!$this->repository;
352
    }
353
}
354