Completed
Push — master ( a246a2...4f4774 )
by Dmitry
02:32
created

Repository::getMapper()   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 0
Metric Value
dl 0
loc 4
ccs 2
cts 2
cp 1
rs 10
c 0
b 0
f 0
cc 1
eloc 2
nc 1
nop 0
crap 1
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
    private function getInstance($tuple)
147 1
    {
148 64
        $key = $this->space->getTupleKey($tuple);
149
150
        if (array_key_exists($key, $this->persisted)) {
151
            return $this->persisted[$key];
152
        }
153 64
154 64
        $class = Entity::class;
155 64 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...
156
            $entityClass = $plugin->getEntityClass($this->space);
157
            if ($entityClass) {
158 64
                if ($class != Entity::class) {
159 64
                    throw new Exception('Entity class override');
160 2
                }
161
                $class = $entityClass;
162
            }
163 64
        }
164
        $instance = new $class($this);
165 64
166 1
        $this->original[$key] = $tuple;
167 1
168 1 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...
169 1
            $instance->{$info['name']} = array_key_exists($index, $tuple) ? $tuple[$index] : null;
170 1
        }
171
172
        $this->keys->offsetSet($instance, $key);
173 1
174
        return $this->persisted[$key] = $instance;
175
    }
176 1
177
    public function getMapper()
178 1
    {
179 1
        return $this->space->getMapper();
180
    }
181 1
182
    public function knows($instance)
183
    {
184 64
        return $this->keys->offsetExists($instance);
185
    }
186 64
187 64
    public function update(Entity $instance, $operations)
188 64
    {
189 64
        if (!count($operations)) {
190 64
            return;
191 64
        }
192 64
193
        $tupleOperations = [];
194 64
        foreach ($operations as $operation) {
195
            $tupleIndex = $this->space->getPropertyIndex($operation[1]);
196 64
            $tupleOperations[] = [$operation[0], $tupleIndex, $operation[2]];
197 64
        }
198
199 14
        $pk = [];
200
        foreach ($this->space->getPrimaryIndex()->parts as $part) {
201
            $pk[] = $instance->{$this->space->getFormat()[$part[0]]['name']};
202 64
        }
203 64
204
        $client = $this->getMapper()->getClient();
205 17
        $result = $client->getSpace($this->space->getId())->update($pk, $tupleOperations);
206
        foreach ($result->getData() as $tuple) {
207 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...
208
                if (array_key_exists($index, $tuple)) {
209
                    $instance->{$info['name']} = $tuple[$index];
210
                }
211
            }
212 64
        }
213
    }
214 64
215
    public function truncate()
216
    {
217 6
        $this->results = [];
218
        $id = $this->space->getId();
219 6
        $this->getMapper()->getClient()->evaluate("box.space[$id]:truncate()");
220 6
    }
221 6
222
    public function remove($params = [])
223 6
    {
224 6
        if ($params instanceof Entity) {
225
            return $this->removeEntity($params);
226 1
        }
227
228 1
        if (!count($params)) {
229 1
            throw new Exception("Use truncate to flush space");
230 1
        }
231
232 1
        foreach ($this->find($params) as $entity) {
233 1
            $this->removeEntity($entity);
234
        }
235 64
    }
236
237 64
    public function removeEntity(Entity $instance)
238 64
    {
239
        $key = $this->space->getInstanceKey($instance);
240 64
241
        if (!array_key_exists($key, $this->original)) {
242 64
            return;
243 1
        }
244
245
        if (array_key_exists($key, $this->persisted)) {
246 64
            unset($this->persisted[$key]);
247 64
248 64
            $pk = [];
249 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...
250
                $pk[] = $this->original[$key][$part[0]];
251 19
            }
252 19
            foreach ($this->getMapper()->getPlugins() as $plugin) {
253 19
                $plugin->beforeRemove($instance, $this->space);
254 19
            }
255
256
            $this->getMapper()->getClient()
257 19
                ->getSpace($this->space->getId())
258 19
                ->delete($pk);
259 4
        }
260 19
261 19
        unset($this->original[$key]);
262
263
        $this->results = [];
264
    }
265
266 19
    public function save($instance)
267 19
    {
268 1
        $tuple = [];
269 19
270
        $format = $this->space->getFormat();
271
272
        // complete indexes fields
273 18
        foreach ($this->space->getIndexes() as $index) {
274 18
            foreach ($index->parts as $part) {
275 18
                $name = $format[$part[0]]['name'];
276 18
                if (!property_exists($instance, $name)) {
277
                    $instance->{$name} = null;
278
                }
279 18
            }
280 17
        }
281 17
282 17
        $size = count(get_object_vars($instance));
283 1
        $skipped = 0;
284 1
285 1
        foreach ($format as $index => $info) {
286 1
            if (!property_exists($instance, $info['name'])) {
287
                $skipped++;
288 18
                $instance->{$info['name']} = null;
289
            }
290
291
            $instance->{$info['name']} = $this->getMapper()->getSchema()
292 64
                ->formatValue($info['type'], $instance->{$info['name']});
293
            $tuple[$index] = $instance->{$info['name']};
294 64
295
            if (count($tuple) == $size + $skipped) {
296
                break;
297 64
            }
298
        }
299 64
300 64
        $key = $this->space->getInstanceKey($instance);
301
        $client = $this->getMapper()->getClient();
302 64
303 64
        if (array_key_exists($key, $this->persisted)) {
304
            // update
305
            $update = array_diff_assoc($tuple, $this->original[$key]);
306 64
            if (!count($update)) {
307 64
                return $instance;
308
            }
309
310 64
            $operations = [];
311
            foreach ($update as $index => $value) {
312
                $operations[] = ['=', $index, $value];
313 64
            }
314
315 64
            $pk = [];
316 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...
317 64
                $pk[] = $this->original[$key][$part[0]];
318
            }
319 64
320 64
            foreach ($this->getMapper()->getPlugins() as $plugin) {
321 64
                $plugin->beforeUpdate($instance, $this->space);
322 64
            }
323 64
324
            $client->getSpace($this->space->getId())->update($pk, $operations);
325 64
            $this->original[$key] = $tuple;
326
        } else {
327
            foreach ($this->getMapper()->getPlugins() as $plugin) {
328 64
                $plugin->beforeCreate($instance, $this->space);
329 64
            }
330 64
331 64
            $client->getSpace($this->space->getId())->insert($tuple);
332 64
            $this->persisted[$key] = $instance;
333
            $this->original[$key] = $tuple;
334 64
        }
335
336 64
337
        $this->flushCache();
338 64
    }
339
340
    public function flushCache()
341 64
    {
342
        $this->results = [];
343 64
    }
344
}
345