Completed
Push — master ( 287b76...a76481 )
by Dmitry
03:11
created

Type::getCompleteTuple()   C

Complexity

Conditions 8
Paths 8

Size

Total Lines 28
Code Lines 16

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 15
CRAP Score 8

Importance

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