Completed
Push — master ( 6d4316...84d771 )
by Dmitry
03:34
created

Type::dropIndex()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

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