Passed
Push — master ( a290cb...57bf28 )
by Dawid
02:40
created

Manager::hydrate()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
nc 1
nop 2
dl 0
loc 5
ccs 2
cts 2
cp 1
crap 1
rs 10
c 0
b 0
f 0
1
<?php declare(strict_types=1);
2
3
namespace Igni\Storage;
4
5
use Cache\Adapter\PHPArray\ArrayCachePool;
6
use Igni\Storage\Exception\HydratorException;
7
use Igni\Storage\Exception\RepositoryException;
8
use Igni\Storage\Hydration\HydratorAutoGenerate;
9
use Igni\Storage\Hydration\HydratorFactory;
10
use Igni\Storage\Hydration\ObjectHydrator;
11
use Igni\Storage\Mapping\IdentityMap;
12
use Igni\Storage\Mapping\MetaData\EntityMetaData;
13
use Igni\Storage\Mapping\MetaData\MetaDataFactory;
14
use Igni\Storage\Mapping\MetaData\Strategy\AnnotationMetaDataFactory;
15
use Psr\SimpleCache\CacheInterface;
16
17
class Manager implements IdentityMap, RepositoryContainer, MetaDataFactory
18
{
19
    /** @var Storable[] */
20
    private $registry = [];
21
22
    /** @var Repository[] */
23
    private $repositories = [];
24
25
    /** @var HydratorFactory */
26
    private $hydratorFactory;
27
28
    /** @var ObjectHydrator[] */
29
    private $hydrators = [];
30
31
    /** @var MetaDataFactory */
32
    private $metaDataFactory;
33
34
    /** @var string */
35
    private $hydratorDir;
36
37
    /** @var string */
38
    private $hydratorNamespace;
39
40
    /** @var CacheInterface */
41
    private $cache;
42
43
    /**
44
     * EntityManager constructor.
45
     * @param string|null $hydratorDir
46
     * @param string|null $hydratorNamespace
47
     * @param CacheInterface|null $cache
48
     * @param string|null $hydratorAutoGenerate
49
     * @param MetaDataFactory|null $metaDataFactory
50
     */
51 29
    public function __construct(
52
        string $hydratorDir = null,
53
        string $hydratorNamespace = null,
54
        CacheInterface $cache = null,
55
        string $hydratorAutoGenerate = null,
56
        MetaDataFactory $metaDataFactory = null
57
    ) {
58 29
        if ($hydratorDir === null) {
59 1
            $hydratorDir = sys_get_temp_dir();
60
        }
61
62 29
        if ($hydratorAutoGenerate === null) {
63 29
            $hydratorAutoGenerate = HydratorAutoGenerate::ALWAYS;
64
        }
65
66 29
        if (!is_writable($hydratorDir)) {
67
            throw new HydratorException("Hydrators cannot be generated, directory ($hydratorDir) is not writable.");
68
        }
69
70 29
        if ($metaDataFactory === null) {
71 29
            $metaDataFactory = new AnnotationMetaDataFactory();
72
        }
73
74 29
        if ($cache === null) {
75 29
            $cache = new ArrayCachePool();
76
        }
77
78 29
        $this->cache = $cache;
79 29
        $this->hydratorDir = $hydratorDir;
80 29
        $this->metaDataFactory = $metaDataFactory;
81 29
        $this->hydratorNamespace = $hydratorNamespace ?? '';
82
83 29
        $this->hydratorFactory = new HydratorFactory($this, $hydratorAutoGenerate);
84 29
    }
85
86 2
    public function getHydratorDir(): string
87
    {
88 2
        return $this->hydratorDir;
89
    }
90
91 18
    public function getHydratorNamespace(): string
92
    {
93 18
        return $this->hydratorNamespace;
94
    }
95
96
    /**
97
     * Creates new entity in the storage.
98
     *
99
     * @param Storable $entity
100
     * @return Storable
101
     */
102 2
    public function create(Storable $entity): Storable
103
    {
104 2
        $this->getRepository(get_class($entity))->create($entity);
105 2
        $this->attach($entity);
106
107 2
        return $entity;
108
    }
109
110
    /**
111
     * Updated entity in the storage.
112
     *
113
     * @param Storable $entity
114
     * @return Storable
115
     */
116 1
    public function update(Storable $entity): Storable
117
    {
118 1
        $this->getRepository(get_class($entity))->update($entity);
119
120 1
        return $entity;
121
    }
122
123
    /**
124
     * Removes entity from the storage.
125
     *
126
     * @param Storable $entity
127
     * @return Storable
128
     */
129 2
    public function remove(Storable $entity): Storable
130
    {
131 2
        $this->getRepository(get_class($entity))->remove($entity);
132 2
        $this->detach($entity);
133
134 2
        return $entity;
135
    }
136
137
    /**
138
     * Retrieves entity by identifier from the storage.
139
     *
140
     * @param string $entity
141
     * @param $id
142
     * @return Storable
143
     */
144 11
    public function get(string $entity, $id): Storable
145
    {
146 11
        $key = $this->getId($entity, $id);
147
148 11
        if ($this->has($entity, $id)) {
149 1
            return $this->registry[$key];
150
        }
151
152 11
        return $this->getRepository($entity)->get($id);
153
    }
154
155 13
    public function getRepository(string $entity): Repository
156
    {
157 13
        if ($this->hasRepository($entity)) {
158 13
            return $this->repositories[$entity];
159
        }
160
161
        throw RepositoryException::forNotRegisteredRepository($entity);
162
    }
163
164 13
    public function hasRepository(string $entity): bool
165
    {
166 13
        return isset($this->repositories[$entity]);
167
    }
168
169 18
    public function addRepository(Repository ...$repositories): void
170
    {
171 18
        foreach ($repositories as $repository) {
172 18
            $this->repositories[$repository->getEntityClass()] = $repository;
173
        }
174 18
    }
175
176 11
    public function attach(Storable $entity): Storable
177
    {
178 11
        $key = $this->getId($entity);
179
180 11
        if (!isset($this->registry[$key])) {
181 11
            $this->registry[$key] = $entity;
182
        }
183
184 11
        return $this->registry[$key];
185
    }
186
187 2
    public function detach(Storable $entity): Storable
188
    {
189 2
        $key = $this->getId($entity);
190 2
        if (isset($this->registry[$key])) {
191 2
            unset($this->registry[$key]);
192
        }
193
194 2
        return $entity;
195
    }
196
197
    /**
198
     *
199
     * @param string $class
200
     * @param $id
201
     * @return bool
202
     */
203 13
    public function has(string $class, $id): bool
204
    {
205 13
        $key = $this->getId($class, $id);
206 13
        return isset($this->registry[$key]);
207
    }
208
209
    /**
210
     * Checks if entity lives in the identity map.
211
     *
212
     * @param Storable $entity
213
     * @return bool
214
     */
215 2
    public function contains(Storable $entity): bool
216
    {
217 2
        return in_array($entity, $this->registry, true);
218
    }
219
220
    /**
221
     * Clears identity map.
222
     */
223 1
    public function clear(): void
224
    {
225 1
        $this->registry = [];
226 1
    }
227
228
    /**
229
     * Gets global id for entity which is used for
230
     * later storage in the identity map.
231
     *
232
     * @param $entity
233
     * @param null $id
0 ignored issues
show
Documentation Bug introduced by
Are you sure the doc-type for parameter $id is correct as it would always require null to be passed?
Loading history...
234
     * @return string
235
     */
236 13
    private function getId($entity, $id = null): string
237
    {
238 13
        if ($entity instanceof Storable) {
239 11
            return get_class($entity) . '@' . $entity->getId()->getValue();
240
        }
241
242
        return "${entity}@${id}";
243
    }
244
245
    /**
246
     * Creates instance of the entity class and hydrates it with passed data.
247
     *
248
     * @param string $entityClass
249
     * @param array $data
250
     * @return object
251
     */
252
    public function hydrate(string $entityClass, array $data)
253
    {
254 2
        $hydrator = $this->getHydrator($entityClass);
255
256 2
        return $hydrator->hydrate($data);
257
    }
258
259
    /**
260
     * Extracts data from passed entity and returns it.
261
     *
262
     * @param $entity
263
     * @return array
264
     */
265
    public function extract($entity): array
266
    {
267 1
        $entityClass = get_class($entity);
268
269 1
        $hydrator = $this->getHydrator($entityClass);
270
271 1
        return $hydrator->extract($entity);
272
    }
273
274
    /**
275
     * Returns hydrator for the given entity.
276
     *
277
     * @param string $entity
278
     * @return ObjectHydrator
279
     */
280
    public function getHydrator(string $entity): ObjectHydrator
281
    {
282 18
        if (!isset($this->hydrators[$entity])) {
283 18
            $this->hydrators[$entity] = $this->hydratorFactory->get($entity);
284
        }
285
286 18
        return $this->hydrators[$entity];
287
    }
288
289
    /**
290
     * Returns entity's mapping metadata information.
291
     *
292
     * @param string|class $entity
0 ignored issues
show
Bug introduced by
The type Igni\Storage\class was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
293
     * @return EntityMetaData
294
     */
295
    public function getMetaData(string $entity): EntityMetaData
296
    {
297 18
        $key = str_replace('\\', '.', $entity) . '.metadata';
298
299 18
        if (!$this->cache->has($key)) {
300 18
            $metaData = $this->metaDataFactory->getMetaData($entity);
301 18
            $this->cache->set($key, $metaData);
302
        }
303
304 18
        return $this->cache->get($key);
305
    }
306
}
307