Passed
Push — master ( 685595...680e76 )
by Julien
01:32
created

EntityRepository::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 10
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 4
nc 1
nop 4
dl 0
loc 10
ccs 4
cts 4
cp 1
crap 1
rs 9.4285
c 0
b 0
f 0
1
<?php
2
3
namespace Mapado\RestClientSdk;
4
5
use Mapado\RestClientSdk\Exception\SdkException;
6
use Mapado\RestClientSdk\Helper\ArrayHelper;
7
8
class EntityRepository
9
{
10
    /**
11
     * REST Client.
12
     *
13
     * @var RestClient
14
     */
15
    protected $restClient;
16
17
    /**
18
     * SDK Client.
19
     *
20
     * @var SdkClient
21
     */
22
    protected $sdk;
23
24
    /**
25
     * @var string
26
     */
27
    protected $entityName;
28
29
    /**
30
     * classMetadataCache
31
     *
32
     * @var \Mapado\RestClientSdk\Mapping\ClassMetadata
33
     */
34
    private $classMetadataCache;
35
36
    /**
37
     * unitOfWork
38
     *
39
     * @var UnitOfWork
40
     */
41
    private $unitOfWork;
42
43
    /**
44
     * EntityRepository constructor
45
     *
46
     * @param SdkClient  $sdkClient  The client to connect to the datasource with
47
     * @param RestClient $restClient The client to process the http requests
48
     * @param string     $entityName The entity to work with
49
     */
50
    public function __construct(
51
        SdkClient $sdkClient,
52
        RestClient $restClient,
53
        UnitOfWork $unitOfWork,
54
        $entityName
55
    ) {
56 1
        $this->sdk = $sdkClient;
57 1
        $this->restClient = $restClient;
58 1
        $this->unitOfWork = $unitOfWork;
59 1
        $this->entityName = $entityName;
60 1
    }
61
62
    /**
63
     * Adds support for magic finders.
64
     *
65
     * @param string $method
66
     * @param mixed  $arguments
67
     *
68
     * @return array|object the found entity/entities
69
     */
70
    public function __call($method, $arguments)
71
    {
72
        switch (true) {
73 1
            case 0 === strpos($method, 'findBy'):
74 1
                $fieldName = strtolower(substr($method, 6));
75 1
                $methodName = 'findBy';
76 1
                break;
77
78 1
            case 0 === strpos($method, 'findOneBy'):
79 1
                $fieldName = strtolower(substr($method, 9));
80 1
                $methodName = 'findOneBy';
81 1
                break;
82
83
            default:
84
                throw new \BadMethodCallException(
85
                    'Undefined method \'' .
86
                    $method .
87
                    '\'. The method name must start with
88
                    either findBy or findOneBy!'
89
                );
90
        }
91
92 1
        if (empty($arguments)) {
93
            throw new SdkException(
94
                'You need to pass a parameter to ' . $method
95
            );
96
        }
97
98 1
        $mapping = $this->sdk->getMapping();
99 1
        $key = $mapping->getKeyFromModel($this->entityName);
100 1
        $prefix = $mapping->getIdPrefix();
101 1
        $path = empty($prefix) ? '/' . $key : $prefix . '/' . $key;
102
103 1
        if (!empty($fieldName)) {
104 1
            $queryParams = [$fieldName => current($arguments)];
105
        } else {
106 1
            $queryParams = current($arguments);
107
        }
108
        $path .=
109 1
            '?' . http_build_query($this->convertQueryParameters($queryParams));
110
111
        // if entityList is found in cache, return it
112 1
        $entityListFromCache = $this->fetchFromCache($path);
113 1
        if (false !== $entityListFromCache) {
114 1
            return $entityListFromCache;
115
        }
116
117 1
        $data = $this->restClient->get($path);
118
119 1
        $hydrator = $this->sdk->getModelHydrator();
120
121 1
        if ('findOneBy' == $methodName) {
122
            // If more results are found but one is requested return the first hit.
123 1
            $collectionKey = $mapping->getConfig()['collectionKey'];
124 1
            $entityList = ArrayHelper::arrayGet($data, $collectionKey);
0 ignored issues
show
Bug introduced by
It seems like $data can also be of type Psr\Http\Message\ResponseInterface; however, parameter $array of Mapado\RestClientSdk\Hel...ArrayHelper::arrayGet() does only seem to accept array, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

124
            $entityList = ArrayHelper::arrayGet(/** @scrutinizer ignore-type */ $data, $collectionKey);
Loading history...
125 1
            if (!empty($entityList)) {
126 1
                $data = current($entityList);
0 ignored issues
show
Bug introduced by
It seems like $entityList can also be of type Psr\Http\Message\ResponseInterface; however, parameter $array of current() does only seem to accept array, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

126
                $data = current(/** @scrutinizer ignore-type */ $entityList);
Loading history...
127 1
                $hydratedData = $hydrator->hydrate($data, $this->entityName);
128
129 1
                $identifier = $hydratedData->{$this->getClassMetadata()->getIdGetter()}();
130 1
                $this->unitOfWork->registerClean($identifier, $hydratedData);
131 1
                $this->saveToCache($identifier, $hydratedData);
132
            } else {
133 1
                $hydratedData = null;
134
            }
135
        } else {
136 1
            $hydratedData = $hydrator->hydrateList($data, $this->entityName);
0 ignored issues
show
Bug introduced by
It seems like $data can also be of type Psr\Http\Message\ResponseInterface; however, parameter $data of Mapado\RestClientSdk\Mod...Hydrator::hydrateList() does only seem to accept array, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

136
            $hydratedData = $hydrator->hydrateList(/** @scrutinizer ignore-type */ $data, $this->entityName);
Loading history...
137
138
            // then cache each entity from list
139 1
            foreach ($hydratedData as $entity) {
140 1
                $identifier = $entity->{$this->getClassMetadata()->getIdGetter()}();
141 1
                $this->saveToCache($identifier, $entity);
142 1
                $this->unitOfWork->registerClean($identifier, $entity);
143
            }
144
        }
145
146 1
        $this->saveToCache($path, $hydratedData);
147
148 1
        return $hydratedData;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $hydratedData also could return the type Mapado\RestClientSdk\Collection\Collection which is incompatible with the documented return type object|array.
Loading history...
149
    }
150
151
    /**
152
     * find - finds one item of the entity based on the @REST\Id field in the entity
153
     *
154
     * @param string $id          id of the element to fetch
155
     * @param array  $queryParams query parameters to add to the query
156
     *
157
     * @return object
158
     */
159
    public function find($id, $queryParams = [])
160
    {
161 1
        $hydrator = $this->sdk->getModelHydrator();
162 1
        $id = $hydrator->convertId($id, $this->entityName);
163
164 1
        $id = $this->addQueryParameter($id, $queryParams);
165
166
        // if entity is found in cache, return it
167 1
        $entityFromCache = $this->fetchFromCache($id);
168 1
        if (false != $entityFromCache) {
169 1
            return $entityFromCache;
170
        }
171
172 1
        $data = $this->restClient->get($id);
173 1
        $entity = $hydrator->hydrate($data, $this->entityName);
0 ignored issues
show
Bug introduced by
It seems like $data can also be of type Psr\Http\Message\ResponseInterface; however, parameter $data of Mapado\RestClientSdk\Mod...odelHydrator::hydrate() does only seem to accept array, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

173
        $entity = $hydrator->hydrate(/** @scrutinizer ignore-type */ $data, $this->entityName);
Loading history...
174
175
        // cache entity
176 1
        $this->saveToCache($id, $entity);
177 1
        $this->unitOfWork->registerClean($id, $entity);
178
179 1
        return $entity;
180
    }
181
182
    /**
183
     * findAll
184
     *
185
     * @return array|object
186
     */
187
    public function findAll()
188
    {
189 1
        $mapping = $this->sdk->getMapping();
190 1
        $key = $this->getClassMetadata()->getKey();
191 1
        $prefix = $mapping->getIdPrefix();
192 1
        $path = empty($prefix) ? '/' . $key : $prefix . '/' . $key;
193
194 1
        $entityListFromCache = $this->fetchFromCache($path);
195
196
        // if entityList is found in cache, return it
197 1
        if (false !== $entityListFromCache) {
198 1
            return $entityListFromCache;
199
        }
200
201 1
        $data = $this->restClient->get($path);
202
203 1
        $hydrator = $this->sdk->getModelHydrator();
204 1
        $entityList = $hydrator->hydrateList($data, $this->entityName);
0 ignored issues
show
Bug introduced by
It seems like $data can also be of type Psr\Http\Message\ResponseInterface; however, parameter $data of Mapado\RestClientSdk\Mod...Hydrator::hydrateList() does only seem to accept array, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

204
        $entityList = $hydrator->hydrateList(/** @scrutinizer ignore-type */ $data, $this->entityName);
Loading history...
205
206
        // cache entity list
207 1
        $this->saveToCache($path, $entityList);
208
209
        // then cache each entity from list
210 1
        foreach ($entityList as $entity) {
211 1
            $identifier = $entity->{$this->getClassMetadata()->getIdGetter()}();
212 1
            $this->unitOfWork->registerClean($identifier, $entity);
213 1
            $this->saveToCache($identifier, $entity);
214
        }
215
216 1
        return $entityList;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $entityList returns the type Mapado\RestClientSdk\Collection\Collection which is incompatible with the documented return type object|array.
Loading history...
217
    }
218
219
    /**
220
     * remove
221
     *
222
     * @param object $model
223
     *
224
     * @TODO STILL NEEDS TO BE CONVERTED TO ENTITY MODEL
225
     */
226
    public function remove($model)
227
    {
228 1
        $identifier = $model->{$this->getClassMetadata()->getIdGetter()}();
229 1
        $this->removeFromCache($identifier);
230 1
        $this->unitOfWork->clear($identifier);
231
232 1
        $this->restClient->delete($identifier);
233 1
    }
234
235
    /**
236
     * update
237
     *
238
     * @param object $model
239
     *
240
     * @return object
241
     */
242
    public function update(
243
        $model,
244
        $serializationContext = [],
245
        $queryParams = []
246
    ) {
247 1
        $identifier = $model->{$this->getClassMetadata()->getIdGetter()}();
248 1
        $serializer = $this->sdk->getSerializer();
249 1
        $newSerializedModel = $serializer->serialize(
250 1
            $model,
251 1
            $this->entityName,
252 1
            $serializationContext
253
        );
254
255 1
        $oldModel = $this->unitOfWork->getDirtyEntity($identifier);
256 1
        if ($oldModel) {
257 1
            $oldSerializedModel = $serializer->serialize(
258 1
                $oldModel,
259 1
                $this->entityName,
260 1
                $serializationContext
261
            );
262 1
            $newSerializedModel = $this->unitOfWork->getDirtyData(
263 1
                $newSerializedModel,
264 1
                $oldSerializedModel,
265 1
                $this->getClassMetadata()
266
            );
267
        }
268
269 1
        $data = $this->restClient->put(
270 1
            $this->addQueryParameter($identifier, $queryParams),
271 1
            $newSerializedModel
272
        );
273
274 1
        $this->removeFromCache($identifier);
275 1
        $this->unitOfWork->registerClean($identifier, $newSerializedModel);
0 ignored issues
show
Bug introduced by
$newSerializedModel of type array is incompatible with the type object expected by parameter $entity of Mapado\RestClientSdk\UnitOfWork::registerClean(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

275
        $this->unitOfWork->registerClean($identifier, /** @scrutinizer ignore-type */ $newSerializedModel);
Loading history...
276
277 1
        $hydrator = $this->sdk->getModelHydrator();
278
279 1
        return $hydrator->hydrate($data, $this->entityName);
0 ignored issues
show
Bug introduced by
It seems like $data can also be of type Psr\Http\Message\ResponseInterface; however, parameter $data of Mapado\RestClientSdk\Mod...odelHydrator::hydrate() does only seem to accept array, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

279
        return $hydrator->hydrate(/** @scrutinizer ignore-type */ $data, $this->entityName);
Loading history...
280
    }
281
282
    /**
283
     * persist
284
     *
285
     * @param object $model
286
     *
287
     * @return object
288
     */
289
    public function persist(
290
        $model,
291
        $serializationContext = [],
292
        $queryParams = []
293
    ) {
294 1
        $mapping = $this->sdk->getMapping();
295 1
        $prefix = $mapping->getIdPrefix();
296 1
        $key = $mapping->getKeyFromModel($this->entityName);
297
298 1
        $path = empty($prefix) ? '/' . $key : $prefix . '/' . $key;
299
300 1
        $oldSerializedModel = $this->getClassMetadata()->getDefaultSerializedModel();
301 1
        $newSerializedModel = $this->sdk->getSerializer()->serialize(
302 1
            $model,
303 1
            $this->entityName,
304 1
            $serializationContext
305
        );
306
307 1
        $diff = $this->unitOfWork->getDirtyData(
308 1
            $newSerializedModel,
309 1
            $oldSerializedModel,
310 1
            $this->getClassMetadata()
311
        );
312
313 1
        $data = $this->restClient->post(
314 1
            $this->addQueryParameter($path, $queryParams),
315 1
            $diff
316
        );
317
318 1
        $hydrator = $this->sdk->getModelHydrator();
319
320 1
        return $hydrator->hydrate($data, $this->entityName);
0 ignored issues
show
Bug introduced by
It seems like $data can also be of type Psr\Http\Message\ResponseInterface; however, parameter $data of Mapado\RestClientSdk\Mod...odelHydrator::hydrate() does only seem to accept array, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

320
        return $hydrator->hydrate(/** @scrutinizer ignore-type */ $data, $this->entityName);
Loading history...
321
    }
322
323
    /**
324
     * fetchFromCache
325
     *
326
     * @param string $key
327
     *
328
     * @return object|false
329
     */
330
    protected function fetchFromCache($key)
331
    {
332 1
        $key = $this->normalizeCacheKey($key);
333 1
        $cacheItemPool = $this->sdk->getCacheItemPool();
334 1
        if ($cacheItemPool) {
0 ignored issues
show
introduced by
$cacheItemPool is of type Psr\Cache\CacheItemPoolInterface, thus it always evaluated to true.
Loading history...
335 1
            $cacheKey = $this->sdk->getCachePrefix() . $key;
336 1
            if ($cacheItemPool->hasItem($cacheKey)) {
337 1
                $cacheItem = $cacheItemPool->getItem($cacheKey);
338 1
                $cacheData = $cacheItem->get();
339
340 1
                return $cacheData;
341
            }
342
        }
343
344 1
        return false;
345
    }
346
347
    /**
348
     * saveToCache
349
     *
350
     * @return object
351
     */
352
    protected function saveToCache($key, $value)
353
    {
354 1
        $key = $this->normalizeCacheKey($key);
355 1
        $cacheItemPool = $this->sdk->getCacheItemPool();
356 1
        if ($cacheItemPool) {
0 ignored issues
show
introduced by
$cacheItemPool is of type Psr\Cache\CacheItemPoolInterface, thus it always evaluated to true.
Loading history...
357 1
            $cacheKey = $this->sdk->getCachePrefix() . $key;
358
359 1
            if (!$cacheItemPool->hasItem($cacheKey)) {
360 1
                $cacheItem = $cacheItemPool->getItem($cacheKey);
361 1
                $cacheItem->set($value);
362 1
                $cacheItemPool->save($cacheItem);
363
            }
364
        }
365 1
    }
366
367
    /**
368
     * removeFromCache
369
     *
370
     * @param string $key
371
     *
372
     * @return bool true if no cache or cache successfully cleared, false otherwise
373
     */
374
    protected function removeFromCache($key)
375
    {
376 1
        $key = $this->normalizeCacheKey($key);
377 1
        $cacheItemPool = $this->sdk->getCacheItemPool();
378 1
        if ($cacheItemPool) {
0 ignored issues
show
introduced by
$cacheItemPool is of type Psr\Cache\CacheItemPoolInterface, thus it always evaluated to true.
Loading history...
379 1
            $cacheKey = $this->sdk->getCachePrefix() . $key;
380
381 1
            if ($cacheItemPool->hasItem($cacheKey)) {
382 1
                return $cacheItemPool->deleteItem($cacheKey);
383
            }
384
        }
385
386 1
        return true;
387
    }
388
389
    /**
390
     * addQueryParameter
391
     *
392
     * @param string $path path to call
393
     * @param array $params query parameters to add
394
     *
395
     * @return string
396
     */
397
    protected function addQueryParameter($path, $params = [])
398
    {
399 1
        if (empty($params)) {
400 1
            return $path;
401
        }
402
403 1
        return $path . '?' . http_build_query($params);
404
    }
405
406
    /**
407
     * convertQueryParameters
408
     *
409
     * @param array $queryParameters
410
     *
411
     * @return array
412
     */
413
    private function convertQueryParameters($queryParameters)
414
    {
415 1
        $mapping = $this->sdk->getMapping();
416
417 1
        return array_map(function ($item) use ($mapping) {
418 1
            if (is_object($item)) {
419 1
                $classname = get_class($item);
420
421 1
                if ($mapping->hasClassMetadata($classname)) {
422 1
                    $idAttr = $mapping->getClassMetadata(
423 1
                        $classname
424 1
                    )->getIdentifierAttribute();
425
426 1
                    if ($idAttr) {
0 ignored issues
show
introduced by
$idAttr is of type Mapado\RestClientSdk\Mapping\Attribute, thus it always evaluated to true.
Loading history...
427
                        $idGetter =
428 1
                            'get' . ucfirst($idAttr->getAttributeName());
429
430 1
                        return $item->{$idGetter}();
431
                    }
432
                }
433
434 1
                if (method_exists($item, 'getId')) {
435 1
                    return call_user_func([$item, 'getId']);
436
                }
437
            }
438
439 1
            return $item;
440 1
        }, $queryParameters);
441
    }
442
443
    /**
444
     * normalizeCacheKey
445
     *
446
     * @return string
447
     */
448
    private function normalizeCacheKey($key)
449
    {
450 1
        return preg_replace('~[\\/\{\}@:\(\)]~', '_', $key);
451
    }
452
453
    private function getClassMetadata()
454
    {
455 1
        if (!isset($this->classMetadata)) {
0 ignored issues
show
Bug introduced by
The property classMetadata does not exist on Mapado\RestClientSdk\EntityRepository. Did you mean classMetadataCache?
Loading history...
456 1
            $this->classMetadataCache = $this->sdk->getMapping()->getClassMetadata(
457 1
                $this->entityName
458
            );
459
        }
460
461 1
        return $this->classMetadataCache;
462
    }
463
}
464