Completed
Push — master ( 2aad8e...61a907 )
by Dmitry
04:09
created

Type::renameProperty()   B

Complexity

Conditions 4
Paths 4

Size

Total Lines 23
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 14
CRAP Score 4.0047

Importance

Changes 2
Bugs 0 Features 1
Metric Value
c 2
b 0
f 1
dl 0
loc 23
ccs 14
cts 15
cp 0.9333
rs 8.7972
cc 4
eloc 14
nc 4
nop 2
crap 4.0047
1
<?php
2
3
namespace Tarantool\Mapper\Schema;
4
5
use Tarantool\Mapper\Contracts;
6
use LogicException;
7
8
class Type implements Contracts\Type
9
{
10
    private $convention;
11
    private $properties = [];
12
    private $indexes = [];
13
    private $types = [];
14
15
    private $manager;
16
    private $space;
17
    private $spaceId;
18
    private $name;
19
20 50
    public function __construct(Contracts\Manager $manager, $name, array $properties, array $types, array $indexes)
21
    {
22 50
        $this->manager = $manager;
23 50
        $this->name = $name;
24 50
        $this->convention = $manager->getMeta()->getConvention();
25 50
        $this->spaceId = $manager->getSchema()->getSpaceId($name);
26
27 50
        $this->properties = $properties;
28 50
        $this->indexes = $indexes;
29 50
        $this->types = $types;
30 50
    }
31
32 50
    public function getSpace()
33
    {
34 50
        if (!$this->space) {
35 50
            $this->space = $this->getManager()->getClient()->getSpace($this->spaceId);
36
        }
37
38 50
        return $this->space;
39
    }
40
41 50
    public function getSpaceId()
42
    {
43 50
        return $this->spaceId;
44
    }
45
46 50
    public function getManager()
47
    {
48 50
        return $this->manager;
49
    }
50
51 50
    public function getName()
52
    {
53 50
        return $this->name;
54
    }
55
56 50
    public function addIndex($properties, array $arguments = null)
57
    {
58 50
        $properties = (array) $properties;
59 50
        foreach ($properties as $property) {
60 50
            if (!$this->hasProperty($property)) {
61 50
                throw new LogicException("Unknown property $property for ".$this->name);
62
            }
63
        }
64
65 50
        $schema = $this->manager->getSchema();
66
67 50
        $indexName = implode('_', $properties);
68 50
        if (strlen($indexName) > 32) {
69 1
            $indexName = md5($indexName);
70 1
            while (is_numeric($indexName[0])) {
71 1
                $indexName = md5($indexName);
72
            }
73
        }
74
75 50
        if ($schema->hasIndex($this->getName(), $indexName)) {
76 1
            throw new LogicException("Index $indexName already exists!");
77
        }
78
79 50
        if (!$arguments) {
80 50
            $arguments = [];
81
        }
82
83 50
        if (!array_key_exists('parts', $arguments) || !count($arguments['parts'])) {
84 50
            $arguments['parts'] = [];
85 50
            foreach ($properties as $property) {
86 50
                $arguments['parts'][] = array_search($property, $this->properties) + 1;
87 50
                $arguments['parts'][] = $this->convention->getTarantoolType($this->types[$property]);
88
            }
89
        }
90
91 50
        $num = $schema->createIndex($this->getName(), $indexName, $arguments);
92 50
        $this->indexes[$num] = $properties;
93
94 50
        return $this;
95
    }
96
97
    /**
98
     * @param $property name
99
     *
100
     * @return Type
101
     */
102 50
    public function addProperty($name, $type = null)
103
    {
104 50
        if ($this->hasProperty($name)) {
105 1
            throw new LogicException("Duplicate property $name");
106
        }
107 50
        if (!$type) {
108 50
            $type = $this->manager->getMeta()->getConvention()->getType($name);
109
        }
110 50
        $this->types[$name] = $type;
111 50
        $property = $this->manager->create('property', [
112 50
            'space' => $this->spaceId,
113 50
            'index' => count($this->properties),
114 50
            'name' => $name,
115 50
            'type' => $this->types[$name],
116
        ]);
117
118 50
        $this->properties[$property->index] = $name;
0 ignored issues
show
Bug introduced by
Accessing index on the interface Tarantool\Mapper\Contracts\Entity suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
119
120 50
        return $this;
121
    }
122
123 50
    public function hasProperty($name)
124
    {
125 50
        return in_array($name, $this->properties);
126
    }
127
128 50
    public function getProperties()
129
    {
130 50
        return $this->properties;
131
    }
132
133 2
    public function getPropertyType($property)
134
    {
135 2
        return $this->types[$property];
136
    }
137
138 9
    public function setPropertyType($property, $type)
139
    {
140 9
        if (is_array($property)) {
141 2
            foreach ($property as $prop) {
142 2
                $this->setPropertyType($prop, $type);
143
            }
144
145 2
            return $this;
146
        }
147
148 9
        $this->types[$property] = $type;
149
150
        // update entity
151 9
        $row = $this->getManager()->get('property')->findOne([
0 ignored issues
show
Bug introduced by
The method findOne does only exist in Tarantool\Mapper\Contracts\Repository, but not in Tarantool\Mapper\Contracts\Entity.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
152 9
            'space' => $this->spaceId,
153 9
            'index' => array_search($property, $this->properties),
154
        ]);
155 9
        $row->type = $type;
156 9
        $this->getManager()->save($row);
157
158 9
        return $this;
159
    }
160
161 1
    public function renameProperty($name, $new)
162
    {
163 1
        if (!$this->hasProperty($name)) {
164
            throw new LogicException("Unknown property $name");
165
        }
166
167 1
        $index = array_search($name, $this->properties);
168 1
        unset($this->properties[$index]);
169 1
        $this->properties[$index] = $new;
170
171 1
        $row = $this->getManager()->get('property')->findOne([
0 ignored issues
show
Bug introduced by
The method findOne does only exist in Tarantool\Mapper\Contracts\Repository, but not in Tarantool\Mapper\Contracts\Entity.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
172 1
            'space' => $this->spaceId,
173 1
            'index' => $index,
174
        ]);
175 1
        $row->name = $new;
176 1
        $this->getManager()->save($row);
177
178 1
        foreach ($this->indexes as $index => $fields) {
179 1
            if(in_array($name, $fields)) {
180 1
                $this->indexes[$index][array_search($name, $fields)] = $new;
181
            }
182
        }
183 1
    }
184
185 5
    public function removeProperty($name)
186
    {
187 5
        if (!$this->hasProperty($name)) {
188
            throw new LogicException("Unknown property $name");
189
        }
190
191 5
        foreach ($this->indexes as $index => $fields) {
192 3
            if ($fields != [$name] && in_array($name, $fields)) {
193 3
                throw new LogicException("Property is used by composite index $index");
194
            }
195
        }
196
197 4
        foreach ($this->indexes as $index => $fields) {
198 2
            if ($fields == [$name]) {
199 2
                unset($this->indexes[$index]);
200
            }
201
        }
202
203 4
        $index = array_search($name, $this->properties);
204
205 4
        unset($this->properties[$index]);
206 4
        unset($this->types[$name]);
207
208 4
        $property = $this->manager->get('property')->findOne([
0 ignored issues
show
Bug introduced by
The method findOne does only exist in Tarantool\Mapper\Contracts\Repository, but not in Tarantool\Mapper\Contracts\Entity.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
209 4
            'space' => $this->spaceId,
210 4
            'index' => $index,
211
        ]);
212
213 4
        $this->manager->remove($property);
214 4
    }
215
216 6
    public function reference(Contracts\Type $foreign, $property = null)
217
    {
218 6
        if (!$property) {
219 5
            $property = $foreign->getName();
220
        }
221
222 6
        $this->addProperty($property);
223 6
        $this->setPropertyType($property, $foreign->getName());
224 6
        $this->addIndex($property, ['unique' => false]);
225
226 6
        return $this;
227
    }
228
229 50
    public function isReference($property)
230
    {
231 50
        return !$this->convention->isPrimitive($this->types[$property]);
232
    }
233
234 4
    public function getReferenceProperty(Contracts\Type $type)
235
    {
236 4
        $properties = [];
237 4
        foreach ($this->types as $property => $propertyType) {
238 4
            if ($type->getName() == $propertyType) {
239 4
                $properties[] = $property;
240
            }
241
        }
242 4
        if (!count($properties)) {
243 1
            throw new LogicException('Type '.$this->getName().' is not related with '.$type->getName());
244
        }
245 3
        if (count($properties) > 1) {
246 1
            throw new LogicException('Multiple type reference found');
247
        }
248
249 2
        return $properties[0];
250
    }
251
252 1
    public function getReferences()
253
    {
254 1
        $references = [];
255 1
        $convention = $this->manager->getMeta()->getConvention();
256 1
        foreach ($this->types as $property => $type) {
257 1
            if (!$convention->isPrimitive($type)) {
258 1
                $references[$property] = $type;
259
            }
260
        }
261
262 1
        return $references;
263
    }
264
265 50
    public function getRequiredProperties()
266
    {
267 50
        if (!isset($this->requiredProperties)) {
268 50
            $this->requiredProperties = ['id' => 1];
0 ignored issues
show
Bug introduced by
The property requiredProperties does not seem to exist. Did you mean properties?

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...
269 50
            $indexList = $this->manager->getSchema()->listIndexes($this->getName());
270 50
            foreach ($indexList as $name => $fields) {
271 50
                foreach ($fields as $num) {
272 50
                    $this->requiredProperties[$this->properties[$num]] = true;
0 ignored issues
show
Bug introduced by
The property requiredProperties does not seem to exist. Did you mean properties?

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...
273
                }
274
            }
275 50
            $this->requiredProperties = array_keys($this->requiredProperties);
0 ignored issues
show
Bug introduced by
The property requiredProperties does not seem to exist. Did you mean properties?

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...
276
        }
277
278 50
        return $this->requiredProperties;
0 ignored issues
show
Bug introduced by
The property requiredProperties does not seem to exist. Did you mean properties?

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...
279
    }
280
281 1
    public function getIndex($num)
282
    {
283 1
        return $this->indexes[$num];
284
    }
285
286 3
    public function dropIndex($num)
287
    {
288 3
        if (is_array($num)) {
289 1
            $num = $this->findIndex($num);
290
        }
291 3
        $this->getManager()->getSchema()->dropIndex($this->spaceId, $num);
292 3
        unset($this->indexes[$num]);
293 3
    }
294
295 3
    public function getIndexes()
296
    {
297 3
        return $this->indexes;
298
    }
299
300 50
    public function findIndex($query)
301
    {
302 50
        if (!count($query)) {
303 5
            return 0;
304
        }
305
306 50
        sort($query);
307 50
        foreach ($this->indexes as $name => $fields) {
308 50
            if ($fields == $query) {
309 50
                return $name;
310
            }
311
        }
312
313
        // cast partial index
314 6
        $casting = [];
315
316 6
        foreach ($this->indexes as $indexId => $fields) {
317 6
            if (!count(array_diff($query, $fields))) {
318 5
                for ($i = 0; $i < count($query); ++$i) {
0 ignored issues
show
Performance Best Practice introduced by
It seems like you are calling the size function count() as part of the test condition. You might want to compute the size beforehand, and not on each iteration.

If the size of the collection does not change during the iteration, it is generally a good practice to compute it beforehand, and not on each iteration:

for ($i=0; $i<count($array); $i++) { // calls count() on each iteration
}

// Better
for ($i=0, $c=count($array); $i<$c; $i++) { // calls count() just once
}
Loading history...
319 5
                    if (!in_array($fields[$i], $query)) {
320 1
                        continue 2;
321
                    }
322
                }
323 6
                $casting[count(array_diff($fields, $query))] = $indexId;
324
            }
325
        }
326 6
        if (!count($casting)) {
327 2
            return;
328
        }
329 5
        ksort($casting);
330
331 5
        return array_shift($casting);
332
    }
333
334 50
    public function getIndexTuple($index, $params)
335
    {
336 50
        $tuple = [];
337 50
        foreach ($this->indexes[$index] as $property) {
338 50
            if (array_key_exists($property, $params)) {
339 50
                $tuple[array_search($property, $this->indexes[$index])] = $params[$property];
340
            }
341
        }
342
343 50
        return $tuple;
344
    }
345
346 50
    public function getCompleteTuple($input)
347
    {
348 50
        $tuple = $this->getTuple($input);
349 50
        $required = $this->getRequiredProperties();
350
351 50
        foreach ($this->getProperties() as $index => $field) {
352 50
            if (in_array($field, $required) && !array_key_exists($index, $tuple)) {
353 1
                if ($this->isReference($field)) {
354 1
                    $tuple[$index] = 0;
355
                } else {
356 50
                    $tuple[$index] = '';
357
                }
358
            }
359
        }
360
361
        // normalize tuple
362 50
        if (array_values($tuple) != $tuple) {
363
            // index was skipped
364 3
            $max = max(array_keys($tuple));
365 3
            foreach (range(0, $max) as $index) {
366 3
                if (!array_key_exists($index, $tuple)) {
367 3
                    $tuple[$index] = null;
368
                }
369
            }
370 3
            ksort($tuple);
371
        }
372
373 50
        return $tuple;
374
    }
375
376 50 View Code Duplication
    public function getTuple($input)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
377
    {
378 50
        $output = [];
379 50
        foreach ($this->getProperties() as $index => $name) {
380 50
            if (array_key_exists($name, $input)) {
381 50
                $output[$index] = $this->encodeProperty($name, $input[$name]);
382
            }
383
        }
384
385 50
        return $output;
386
    }
387
388 50 View Code Duplication
    public function fromTuple($input)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
389
    {
390 50
        $output = [];
391 50
        foreach ($this->getProperties() as $index => $name) {
392 50
            if (array_key_exists($index, $input)) {
393 50
                $output[$name] = $this->decodeProperty($name, $input[$index]);
394
            }
395
        }
396
397 50
        return $output;
398
    }
399
400 50
    public function encodeProperty($name, $value)
401
    {
402 50
        return $this->convention->encode($this->types[$name], $value);
403
    }
404
405 50
    public function decodeProperty($name, $value)
406
    {
407 50
        return $this->convention->decode($this->types[$name], $value);
408
    }
409
}
410