Completed
Push — master ( e4e252...9e06e2 )
by Dmitry
02:48
created

Type::getTuple()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 11
Code Lines 6

Duplication

Lines 11
Ratio 100 %

Code Coverage

Tests 6
CRAP Score 3
Metric Value
dl 11
loc 11
ccs 6
cts 6
cp 1
rs 9.4285
cc 3
eloc 6
nc 3
nop 1
crap 3
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 26
    public function __construct(Contracts\Manager $manager, $name, array $properties, array $types, array $indexes)
21
    {
22 26
        $this->manager = $manager;
23 26
        $this->name = $name;
24 26
        $this->convention = $manager->getMeta()->getConvention();
25 26
        $this->spaceId = $manager->getSchema()->getSpaceId($name);
26
27 26
        $this->properties = $properties;
28 26
        $this->indexes = $indexes;
29 26
        $this->types = $types;
30 26
    }
31
32 26
    public function getSpace()
33
    {
34 26
        if (!$this->space) {
35 26
            $this->space = $this->getManager()->getClient()->getSpace($this->spaceId);
36
        }
37
38 26
        return $this->space;
39
    }
40
41 26
    public function getSpaceId()
42
    {
43 26
        return $this->spaceId;
44
    }
45
46 26
    public function getManager()
47
    {
48 26
        return $this->manager;
49
    }
50
51 26
    public function getName()
52
    {
53 26
        return $this->name;
54
    }
55
56 26
    public function addIndex($properties, array $arguments = null)
57
    {
58 26
        $properties = (array) $properties;
59 26
        foreach ($properties as $property) {
60 26
            if (!$this->hasProperty($property)) {
61 26
                throw new LogicException("Unknown property $property for ".$this->name);
62
            }
63
        }
64
65 26
        $schema = $this->manager->getSchema();
66
67 26
        $indexName = implode('_', $properties);
68
69 26
        if ($schema->hasIndex($this->getName(), $indexName)) {
70 1
            throw new LogicException("Index $indexName already exists!");
71
        }
72
73 26
        if (!$arguments) {
74 26
            $arguments = [];
75
        }
76
77 26
        if (!array_key_exists('parts', $arguments) || !count($arguments['parts'])) {
78 26
            $arguments['parts'] = [];
79 26
            foreach ($properties as $property) {
80 26
                $arguments['parts'][] = array_search($property, $this->properties) + 1;
81 26
                $arguments['parts'][] = $this->convention->getTarantoolType($this->types[$property]);
82
            }
83
        }
84
85 26
        $schema->createIndex($this->getName(), $indexName, $arguments);
86 26
        $this->indexes[$indexName] = $properties;
87
88 26
        return $this;
89
    }
90
91
    /**
92
     * @param $property name
93
     *
94
     * @return Type
95
     */
96 26
    public function addProperty($name, $type = null)
97
    {
98 26
        if ($this->hasProperty($name)) {
99 1
            throw new LogicException("Duplicate property $name");
100
        }
101 26
        if (!$type) {
102 26
            $type = $this->manager->getMeta()->getConvention()->getType($name);
103
        }
104 26
        $this->types[$name] = $type;
105 26
        $this->manager->create('property', [
106 26
            'space' => $this->spaceId,
107 26
            'index' => count($this->properties),
108 26
            'name' => $name,
109 26
            'type' => $this->types[$name],
110
        ]);
111
112 26
        $this->properties[] = $name;
113
114 26
        return $this;
115
    }
116
117 26
    public function hasProperty($name)
118
    {
119 26
        return in_array($name, $this->properties);
120
    }
121
122 26
    public function getProperties()
123
    {
124 26
        return $this->properties;
125
    }
126
127 1
    public function getPropertyType($property)
128
    {
129 1
        return $this->types[$property];
130
    }
131
132 4
    public function setPropertyType($property, $type)
133
    {
134 4
        $this->types[$property] = $type;
135
136
        // update entity
137 4
        $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...
138 4
            'space' => $this->spaceId,
139 4
            'index' => array_search($property, $this->properties),
140
        ]);
141 4
        $row->type = $type;
142 4
        $this->getManager()->save($row);
143
144 4
        return $this;
145
    }
146
147 3
    public function reference(Contracts\Type $foreign, $property = null)
148
    {
149 3
        if (!$property) {
150 2
            $property = $foreign->getName();
151
        }
152
153 3
        $this->addProperty($property);
154 3
        $this->setPropertyType($property, $foreign->getName());
155 3
        $this->addIndex($property, ['unique' => false]);
156
157 3
        return $this;
158
    }
159
160 1
    public function isReference($property)
161
    {
162 1
        return !$this->convention->isPrimitive($this->types[$property]);
163
    }
164
165 3
    public function getReferenceProperty(Contracts\Type $type)
166
    {
167 3
        $properties = [];
168 3
        foreach ($this->types as $property => $propertyType) {
169 3
            if ($type->getName() == $propertyType) {
170 3
                $properties[] = $property;
171
            }
172
        }
173 3
        if (!count($properties)) {
174 1
            throw new LogicException('Type '.$this->getName().' is not related with '.$type->getName());
175
        }
176 2
        if (count($properties) > 1) {
177 1
            throw new LogicException('Multiple type reference found');
178
        }
179
180 1
        return $properties[0];
181
    }
182
183 1
    public function getReferences()
184
    {
185 1
        $references = [];
186 1
        $convention = $this->manager->getMeta()->getConvention();
187 1
        foreach ($this->types as $property => $type) {
188 1
            if (!$convention->isPrimitive($type)) {
189 1
                $references[$property] = $type;
190
            }
191
        }
192
193 1
        return $references;
194
    }
195
196 26
    public function getRequiredProperties()
197
    {
198 26
        if (!isset($this->requiredProperties)) {
199 26
            $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...
200 26
            $indexList = $this->manager->getSchema()->listIndexes($this->getName());
201 26
            foreach ($indexList as $name => $fields) {
202 26
                foreach ($fields as $num) {
203 26
                    $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...
204
                }
205
            }
206 26
            $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...
207
        }
208
209 26
        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...
210
    }
211
212 26
    public function findIndex($query)
213
    {
214 26
        if (!count($query)) {
215 2
            return 'id';
216
        }
217
218 26
        sort($query);
219 26
        foreach ($this->indexes as $name => $fields) {
220 26
            if ($fields == $query) {
221 26
                return $name;
222
            }
223
        }
224
225
        // cast partial index
226 2
        $casting = [];
227
228 2
        foreach ($this->indexes as $name => $fields) {
229 2
            if (!count(array_diff($query, $fields))) {
230 2
                $casting[count(array_diff($fields, $query))] = $name;
231
            }
232
        }
233 2
        ksort($casting);
234
235 2
        return array_shift($casting);
236
    }
237
238 26
    public function getIndexTuple($index, $params)
239
    {
240 26
        $tuple = [];
241 26
        foreach ($this->indexes[$index] as $property) {
242 26
            $value = array_key_exists($property, $params) ? $params[$property] : null;
243 26
            if ($value) {
244 26
                $tuple[array_search($property, $this->indexes[$index])] = $value;
245
            }
246
        }
247
248 26
        return $tuple;
249
    }
250
251 26 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...
252
    {
253 26
        $output = [];
254 26
        foreach ($this->getProperties() as $index => $name) {
255 26
            if (array_key_exists($name, $input)) {
256 26
                $output[$index] = $this->encodeProperty($name, $input[$name]);
257
            }
258
        }
259
260 26
        return $output;
261
    }
262
263 26 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...
264
    {
265 26
        $output = [];
266 26
        foreach ($this->getProperties() as $index => $name) {
267 26
            if (array_key_exists($index, $input)) {
268 26
                $output[$name] = $this->decodeProperty($name, $input[$index]);
269
            }
270
        }
271
272 26
        return $output;
273
    }
274
275 26
    public function encodeProperty($name, $value)
276
    {
277 26
        return $this->convention->encode($this->types[$name], $value);
278
    }
279
280 26
    public function decodeProperty($name, $value)
281
    {
282 26
        return $this->convention->decode($this->types[$name], $value);
283
    }
284
}
285