Completed
Push — master ( a89b72...2aad8e )
by Dmitry
04:42
created

Type::getManager()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 4
ccs 2
cts 2
cp 1
rs 10
cc 1
eloc 2
nc 1
nop 0
crap 1
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 1
    }
178
179 5
    public function removeProperty($name)
180
    {
181 5
        if (!$this->hasProperty($name)) {
182
            throw new LogicException("Unknown property $name");
183
        }
184
185 5
        foreach ($this->indexes as $index => $fields) {
186 3
            if ($fields != [$name] && in_array($name, $fields)) {
187 3
                throw new LogicException("Property is used by composite index $index");
188
            }
189
        }
190
191 4
        foreach ($this->indexes as $index => $fields) {
192 2
            if ($fields == [$name]) {
193 2
                unset($this->indexes[$index]);
194
            }
195
        }
196
197 4
        $index = array_search($name, $this->properties);
198
199 4
        unset($this->properties[$index]);
200 4
        unset($this->types[$name]);
201
202 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...
203 4
            'space' => $this->spaceId,
204 4
            'index' => $index,
205
        ]);
206
207 4
        $this->manager->remove($property);
208 4
    }
209
210 6
    public function reference(Contracts\Type $foreign, $property = null)
211
    {
212 6
        if (!$property) {
213 5
            $property = $foreign->getName();
214
        }
215
216 6
        $this->addProperty($property);
217 6
        $this->setPropertyType($property, $foreign->getName());
218 6
        $this->addIndex($property, ['unique' => false]);
219
220 6
        return $this;
221
    }
222
223 50
    public function isReference($property)
224
    {
225 50
        return !$this->convention->isPrimitive($this->types[$property]);
226
    }
227
228 4
    public function getReferenceProperty(Contracts\Type $type)
229
    {
230 4
        $properties = [];
231 4
        foreach ($this->types as $property => $propertyType) {
232 4
            if ($type->getName() == $propertyType) {
233 4
                $properties[] = $property;
234
            }
235
        }
236 4
        if (!count($properties)) {
237 1
            throw new LogicException('Type '.$this->getName().' is not related with '.$type->getName());
238
        }
239 3
        if (count($properties) > 1) {
240 1
            throw new LogicException('Multiple type reference found');
241
        }
242
243 2
        return $properties[0];
244
    }
245
246 1
    public function getReferences()
247
    {
248 1
        $references = [];
249 1
        $convention = $this->manager->getMeta()->getConvention();
250 1
        foreach ($this->types as $property => $type) {
251 1
            if (!$convention->isPrimitive($type)) {
252 1
                $references[$property] = $type;
253
            }
254
        }
255
256 1
        return $references;
257
    }
258
259 50
    public function getRequiredProperties()
260
    {
261 50
        if (!isset($this->requiredProperties)) {
262 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...
263 50
            $indexList = $this->manager->getSchema()->listIndexes($this->getName());
264 50
            foreach ($indexList as $name => $fields) {
265 50
                foreach ($fields as $num) {
266 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...
267
                }
268
            }
269 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...
270
        }
271
272 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...
273
    }
274
275 1
    public function getIndex($num)
276
    {
277 1
        return $this->indexes[$num];
278
    }
279
280 3
    public function dropIndex($num)
281
    {
282 3
        if (is_array($num)) {
283 1
            $num = $this->findIndex($num);
284
        }
285 3
        $this->getManager()->getSchema()->dropIndex($this->spaceId, $num);
286 3
        unset($this->indexes[$num]);
287 3
    }
288
289 3
    public function getIndexes()
290
    {
291 3
        return $this->indexes;
292
    }
293
294 50
    public function findIndex($query)
295
    {
296 50
        if (!count($query)) {
297 5
            return 0;
298
        }
299
300 50
        sort($query);
301 50
        foreach ($this->indexes as $name => $fields) {
302 50
            if ($fields == $query) {
303 50
                return $name;
304
            }
305
        }
306
307
        // cast partial index
308 6
        $casting = [];
309
310 6
        foreach ($this->indexes as $indexId => $fields) {
311 6
            if (!count(array_diff($query, $fields))) {
312 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...
313 5
                    if (!in_array($fields[$i], $query)) {
314 1
                        continue 2;
315
                    }
316
                }
317 6
                $casting[count(array_diff($fields, $query))] = $indexId;
318
            }
319
        }
320 6
        if (!count($casting)) {
321 2
            return;
322
        }
323 5
        ksort($casting);
324
325 5
        return array_shift($casting);
326
    }
327
328 50
    public function getIndexTuple($index, $params)
329
    {
330 50
        $tuple = [];
331 50
        foreach ($this->indexes[$index] as $property) {
332 50
            if (array_key_exists($property, $params)) {
333 50
                $tuple[array_search($property, $this->indexes[$index])] = $params[$property];
334
            }
335
        }
336
337 50
        return $tuple;
338
    }
339
340 50
    public function getCompleteTuple($input)
341
    {
342 50
        $tuple = $this->getTuple($input);
343 50
        $required = $this->getRequiredProperties();
344
345 50
        foreach ($this->getProperties() as $index => $field) {
346 50
            if (in_array($field, $required) && !array_key_exists($index, $tuple)) {
347 1
                if ($this->isReference($field)) {
348 1
                    $tuple[$index] = 0;
349
                } else {
350 50
                    $tuple[$index] = '';
351
                }
352
            }
353
        }
354
355
        // normalize tuple
356 50
        if (array_values($tuple) != $tuple) {
357
            // index was skipped
358 3
            $max = max(array_keys($tuple));
359 3
            foreach (range(0, $max) as $index) {
360 3
                if (!array_key_exists($index, $tuple)) {
361 3
                    $tuple[$index] = null;
362
                }
363
            }
364 3
            ksort($tuple);
365
        }
366
367 50
        return $tuple;
368
    }
369
370 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...
371
    {
372 50
        $output = [];
373 50
        foreach ($this->getProperties() as $index => $name) {
374 50
            if (array_key_exists($name, $input)) {
375 50
                $output[$index] = $this->encodeProperty($name, $input[$name]);
376
            }
377
        }
378
379 50
        return $output;
380
    }
381
382 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...
383
    {
384 50
        $output = [];
385 50
        foreach ($this->getProperties() as $index => $name) {
386 50
            if (array_key_exists($index, $input)) {
387 50
                $output[$name] = $this->decodeProperty($name, $input[$index]);
388
            }
389
        }
390
391 50
        return $output;
392
    }
393
394 50
    public function encodeProperty($name, $value)
395
    {
396 50
        return $this->convention->encode($this->types[$name], $value);
397
    }
398
399 50
    public function decodeProperty($name, $value)
400
    {
401 50
        return $this->convention->decode($this->types[$name], $value);
402
    }
403
}
404