Completed
Push — master ( 36fa07...07df51 )
by Dmitry
02:25
created

Repository::find()   C

Complexity

Conditions 14
Paths 113

Size

Total Lines 56
Code Lines 32

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 29
CRAP Score 14

Importance

Changes 0
Metric Value
dl 0
loc 56
ccs 29
cts 29
cp 1
rs 6.2074
c 0
b 0
f 0
cc 14
eloc 32
nc 113
nop 2
crap 14

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
namespace Tarantool\Mapper;
4
5
use Exception;
6
use SplObjectStorage;
7
8
class Repository
9
{
10
    private $space;
11
    private $persisted = [];
12
    private $original = [];
13
    private $keys;
14
15
    private $results = [];
16
17
    public function __construct(Space $space)
18
    {
19
        $this->space = $space;
20
        $this->keys = new SplObjectStorage;
21
    }
22
23 64
    public function create($data)
24
    {
25 64
        $data = (array) $data;
26 64
        $class = Entity::class;
27 View Code Duplication
        foreach ($this->getMapper()->getPlugins() as $plugin) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
28 64
            $entityClass = $plugin->getEntityClass($this->space);
29
            if ($entityClass) {
30 64
                if ($class != Entity::class) {
31 64
                    throw new Exception('Entity class override');
32
                }
33
                $class = $entityClass;
34 64
            }
35 2
        }
36
37
        if (array_key_exists(0, $data)) {
38 64
            $byType = [];
39 64
            foreach ($this->space->getFormat() as $row) {
40 64
                if (!array_key_exists($row['type'], $byType)) {
41 64
                    $byType[$row['type']] = [$row['name']];
42 4
                } else {
43 4
                    $byType[$row['type']][] = $row['name'];
44
                }
45 64
            }
46 64
            $mapping = [
47 64
                'is_numeric' => 'unsigned',
48 64
                'is_string' => 'str',
49
                'is_array' => '*',
50
            ];
51 64
            foreach ($data as $k => $v) {
52 64
                foreach ($mapping as $function => $type) {
53
                    if (call_user_func($function, $v)) {
54 1
                        if (count($byType[$type]) == 1) {
55
                            $data[$byType[$type][0]] = $v;
56
                            unset($data[$k]);
57
                        }
58 64
                    }
59 2
                }
60 2
            }
61
        }
62 64
63
        $instance = new $class($this);
64 64
        foreach ($this->space->getFormat() as $row) {
65 64
            if (array_key_exists($row['name'], $data)) {
66 64
                $instance->{$row['name']} = $data[$row['name']];
67 64
                if ($data[$row['name']] instanceof Entity) {
68 64
                    $instance->{$row['name']} = $instance->{$row['name']}->id;
69
                }
70
            }
71
        }
72 64
73
        foreach ($this->getMapper()->getPlugins() as $plugin) {
74 64
            $plugin->generateKey($instance, $this->space);
75
        }
76
77 64
        // validate instance key
78
        $key = $this->space->getInstanceKey($instance);
79 64
80
        $this->keys[$instance] = $key;
81 64
        return $instance;
82
    }
83
84 64
    public function findOne($params = [])
85
    {
86 64
        return $this->find($params, true);
87 64
    }
88 64
89 64
    public function find($params = [], $one = false)
90
    {
91 64
        $cacheKey = json_encode(func_get_args());
92
93
        if (array_key_exists($cacheKey, $this->results)) {
94
            return $this->results[$cacheKey];
95 1
        }
96
97
        if (!is_array($params)) {
98 19
            $params = [$params];
99
        }
100 19
        if (count($params) == 1 && array_key_exists(0, $params)) {
101
            $primary = $this->space->getPrimaryIndex();
102
            if (count($primary->parts) == 1) {
103 64
                $formatted = $this->getMapper()->getSchema()->formatValue($primary->parts[0][1], $params[0]);
104
                if ($params[0] == $formatted) {
105 64
                    $params = [
106
                        $this->space->getFormat()[$primary->parts[0][0]]['name'] => $params[0]
107 64
                    ];
108 1
                }
109
            }
110
        }
111 64
112 10
        if (array_key_exists('id', $params)) {
113 3
            if (array_key_exists($params['id'], $this->persisted)) {
114
                $instance = $this->persisted[$params['id']];
115
                return $one ? $instance : [$instance];
116 7
            }
117
        }
118 7
119
120
        $index = $this->space->castIndex($params);
121 64
        if (is_null($index)) {
122 2
            throw new Exception("No index for params ".json_encode($params));
123
        }
124
125 64
        $client = $this->getMapper()->getClient();
126 64
        $values = $this->space->getIndexValues($index, $params);
127
128 64
        $data = $client->getSpace($this->space->getId())->select($values, $index)->getData();
129 64
130 18
        $result = [];
131 64
        foreach ($data as $tuple) {
0 ignored issues
show
Bug introduced by
The expression $data of type null|array is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
132
            $instance = $this->getInstance($tuple);
133
            if ($one) {
134
                return $this->results[$cacheKey] = $instance;
135 64
            }
136 1
            $result[] = $instance;
137
        }
138
139 64
        if ($one) {
140 64
            return $this->results[$cacheKey] = null;
141 2
        }
142 2
143
        return $this->results[$cacheKey] = $result;
144 64
    }
145 64
146
    public function forget($id)
147 1
    {
148 64
        if (array_key_exists($id, $this->persisted)) {
149
            unset($this->persisted[$id]);
150
        }
151
    }
152
153 64
    private function getInstance($tuple)
154 64
    {
155 64
        $key = $this->space->getTupleKey($tuple);
156
157
        if (array_key_exists($key, $this->persisted)) {
158 64
            return $this->persisted[$key];
159 64
        }
160 2
161
        $class = Entity::class;
162 View Code Duplication
        foreach ($this->getMapper()->getPlugins() as $plugin) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
163 64
            $entityClass = $plugin->getEntityClass($this->space);
164
            if ($entityClass) {
165 64
                if ($class != Entity::class) {
166 1
                    throw new Exception('Entity class override');
167 1
                }
168 1
                $class = $entityClass;
169 1
            }
170 1
        }
171
        $instance = new $class($this);
172
173 1
        $this->original[$key] = $tuple;
174
175 View Code Duplication
        foreach ($this->space->getFormat() as $index => $info) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
176 1
            $instance->{$info['name']} = array_key_exists($index, $tuple) ? $tuple[$index] : null;
177
        }
178 1
179 1
        $this->keys->offsetSet($instance, $key);
180
181 1
        return $this->persisted[$key] = $instance;
182
    }
183
184 64
    public function getMapper()
185
    {
186 64
        return $this->space->getMapper();
187 64
    }
188 64
189 64
    public function knows($instance)
190 64
    {
191 64
        return $this->keys->offsetExists($instance);
192 64
    }
193
194 64
    public function update(Entity $instance, $operations)
195
    {
196 64
        if (!count($operations)) {
197 64
            return;
198
        }
199 14
200
        $tupleOperations = [];
201
        foreach ($operations as $operation) {
202 64
            $tupleIndex = $this->space->getPropertyIndex($operation[1]);
203 64
            $tupleOperations[] = [$operation[0], $tupleIndex, $operation[2]];
204
        }
205 17
206
        $pk = [];
207
        foreach ($this->space->getPrimaryIndex()->parts as $part) {
208
            $pk[] = $instance->{$this->space->getFormat()[$part[0]]['name']};
209
        }
210
211
        $client = $this->getMapper()->getClient();
212 64
        $result = $client->getSpace($this->space->getId())->update($pk, $tupleOperations);
213
        foreach ($result->getData() as $tuple) {
214 64 View Code Duplication
            foreach ($this->space->getFormat() as $index => $info) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
215
                if (array_key_exists($index, $tuple)) {
216
                    $instance->{$info['name']} = $tuple[$index];
217 6
                }
218
            }
219 6
        }
220 6
    }
221 6
222
    public function truncate()
223 6
    {
224 6
        $this->results = [];
225
        $id = $this->space->getId();
226 1
        $this->getMapper()->getClient()->evaluate("box.space[$id]:truncate()");
227
    }
228 1
229 1
    public function remove($params = [])
230 1
    {
231
        if ($params instanceof Entity) {
232 1
            return $this->removeEntity($params);
233 1
        }
234
235 64
        if (!count($params)) {
236
            throw new Exception("Use truncate to flush space");
237 64
        }
238 64
239
        foreach ($this->find($params) as $entity) {
240 64
            $this->removeEntity($entity);
241
        }
242 64
    }
243 1
244
    public function removeEntity(Entity $instance)
245
    {
246 64
        $key = $this->space->getInstanceKey($instance);
247 64
248 64
        if (!array_key_exists($key, $this->original)) {
249 64
            return;
250
        }
251 19
252 19
        if (array_key_exists($key, $this->persisted)) {
253 19
            unset($this->persisted[$key]);
254 19
255
            $pk = [];
256 View Code Duplication
            foreach ($this->space->getPrimaryIndex()->parts as $part) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
257 19
                $pk[] = $this->original[$key][$part[0]];
258 19
            }
259 4
260 19
            foreach ($this->getMapper()->getPlugins() as $plugin) {
261 19
                $plugin->beforeRemove($instance, $this->space);
262
            }
263
264
            if (method_exists($instance, 'beforeRemove')) {
265
                $instance->beforeRemove();
0 ignored issues
show
Bug introduced by
The method beforeRemove() does not seem to exist on object<Tarantool\Mapper\Entity>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
266 19
            }
267 19
268 1
269 19
            $this->getMapper()->getClient()
270
                ->getSpace($this->space->getId())
271
                ->delete($pk);
272
        }
273 18
274 18
        unset($this->original[$key]);
275 18
276 18
        $this->results = [];
277
    }
278
279 18
    public function save($instance)
280 17
    {
281 17
        $key = $this->space->getInstanceKey($instance);
282 17
        $client = $this->getMapper()->getClient();
283 1
284 1
        if (array_key_exists($key, $this->persisted)) {
285 1
286 1
            // update
287
            $tuple = $this->getTuple($instance);
288 18
            $update = array_diff_assoc($tuple, $this->original[$key]);
289
            if (!count($update)) {
290
                return $instance;
291
            }
292 64
293
            $operations = [];
294 64
            foreach ($update as $index => $value) {
295
                $operations[] = ['=', $index, $value];
296
            }
297 64
298
            $pk = [];
299 64 View Code Duplication
            foreach ($this->space->getPrimaryIndex()->parts as $part) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
300 64
                $pk[] = $this->original[$key][$part[0]];
301
            }
302 64
303 64
            foreach ($this->getMapper()->getPlugins() as $plugin) {
304
                $plugin->beforeUpdate($instance, $this->space);
305
            }
306 64
307 64
            if (method_exists($instance, 'beforeUpdate')) {
308
                $instance->beforeUpdate();
309
            }
310 64
311
            $client->getSpace($this->space->getId())->update($pk, $operations);
312
            $this->original[$key] = $tuple;
313 64
        } else {
314
            $this->addDefaultValues($instance);
315 64
            foreach ($this->getMapper()->getPlugins() as $plugin) {
316 64
                $plugin->beforeCreate($instance, $this->space);
317 64
            }
318
319 64
            if (method_exists($instance, 'beforeCreate')) {
320 64
                $instance->beforeCreate();
321 64
            }
322 64
323 64
            $tuple = $this->getTuple($instance);
324
            $client->getSpace($this->space->getId())->insert($tuple);
325 64
            $this->persisted[$key] = $instance;
326
            $this->original[$key] = $tuple;
327
        }
328 64
329 64
        $this->flushCache();
330 64
331 64
        return $instance;
332 64
    }
333
334 64
    private function addDefaultValues(Entity $instance)
335
    {
336 64
        $format = $this->space->getFormat();
337
338 64
        // complete indexes fields
339
        foreach ($this->space->getIndexes() as $index) {
340
            foreach ($index->parts as $part) {
341 64
                $name = $format[$part[0]]['name'];
342
                if (!property_exists($instance, $name)) {
343 64
                    $instance->{$name} = null;
344
                }
345
            }
346 3
        }
347
    }
348 3
349 3
    private function getTuple(Entity $instance)
350 3
    {
351 2
        $tuple = [];
352 2
353 2
        $size = count(get_object_vars($instance));
354 2
        $skipped = 0;
355
356
        foreach ($this->space->getFormat() as $index => $info) {
357
            if (!property_exists($instance, $info['name'])) {
358 2
                $skipped++;
359
                $instance->{$info['name']} = null;
360
            }
361 3
362
            $instance->{$info['name']} = $this->getMapper()->getSchema()
363
                ->formatValue($info['type'], $instance->{$info['name']});
364
            $tuple[$index] = $instance->{$info['name']};
365
366
            if (count($tuple) == $size + $skipped) {
367
                break;
368
            }
369
        }
370
371
        return $tuple;
372
    }
373
374
    public function sync($id)
375
    {
376
        if (array_key_exists($id, $this->persisted)) {
377
            $tuple = $this->getMapper()->getClient()->getSpace($this->space->getId())->select([$id], 0)->getData()[0];
378
379
            foreach ($this->space->getFormat() as $index => $info) {
380
                $value = array_key_exists($index, $tuple) ? $tuple[$index] : null;
381
                $this->persisted[$id]->{$info['name']} = $value;
382
                $this->original[$id][$index] = $value;
383
            }
384
        }
385
    }
386
387
    public function flushCache()
388
    {
389
        $this->results = [];
390
    }
391
}
392