Completed
Push — master ( f3eb03...b97c16 )
by Dawid
03:07
created

EntityManager::detach()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 8

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 2

Importance

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