Completed
Push — master ( b0bc78...fffed2 )
by Dmitriy
03:02 queued 50s
created

InMemoryRepository::getEvent()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 9
Code Lines 5

Duplication

Lines 9
Ratio 100 %

Importance

Changes 0
Metric Value
dl 9
loc 9
rs 9.6666
c 0
b 0
f 0
cc 2
eloc 5
nc 2
nop 0
1
<?php
2
3
namespace T4webInfrastructure;
4
5
use ArrayObject;
6
use Zend\EventManager\EventManagerInterface;
7
use Zend\EventManager\Event;
8
use T4webDomainInterface\Infrastructure\CriteriaInterface;
9
use T4webDomainInterface\Infrastructure\RepositoryInterface;
10
use T4webDomainInterface\EntityInterface;
11
use T4webDomainInterface\EntityFactoryInterface;
12
use T4webInfrastructure\Event\EntityChangedEvent;
13
14
class InMemoryRepository implements RepositoryInterface
15
{
16
    /**
17
     * @var string
18
     */
19
    protected $entityName;
20
21
    /**
22
     * @var string
23
     */
24
    protected $collectionClass;
25
26
    /**
27
     * @var CriteriaFactory
28
     */
29
    protected $criteriaFactory;
30
31
    /**
32
     * @var EntityFactoryInterface
33
     */
34
    protected $entityFactory;
35
36
    /**
37
     * @var ArrayObject
38
     */
39
    protected $identityMap;
40
41
    /**
42
     * @var ArrayObject
43
     */
44
    protected $identityMapOriginal;
45
46
    /**
47
     * @var EventManagerInterface
48
     */
49
    protected $eventManager;
50
51
    /**
52
     * @var EntityChangedEvent
53
     */
54
    protected $event;
55
56
    protected $primaryKey = 1;
57
58
    /**
59
     * @param string $entityName
60
     * @param string $collectionClass
61
     * @param CriteriaFactory $criteriaFactory
62
     * @param EntityFactoryInterface $entityFactory
63
     * @param EventManagerInterface $eventManager
64
     */
65
    public function __construct(
66
        $entityName,
67
        $collectionClass,
68
        CriteriaFactory $criteriaFactory,
69
        EntityFactoryInterface $entityFactory,
70
        EventManagerInterface $eventManager
71
    ) {
72
        $this->entityName = $entityName;
73
        $this->collectionClass = $collectionClass;
74
        $this->criteriaFactory = $criteriaFactory;
75
        $this->entityFactory = $entityFactory;
76
        $this->identityMap = new ArrayObject();
77
        $this->identityMapOriginal = new ArrayObject();
78
        $this->eventManager = $eventManager;
79
    }
80
81
    /**
82
     * @param EntityInterface $entity
83
     * @return EntityInterface|int|null
84
     */
85
    public function add(EntityInterface $entity)
86
    {
87
        $id = $entity->getId();
88
89
        if ($this->identityMap->offsetExists((int)$id)) {
90
            $e = $this->getEvent();
91
            $originalEntity = $this->identityMapOriginal->offsetGet($entity->getId());
92
            $e->setOriginalEntity($originalEntity);
93
            $e->setChangedEntity($entity);
94
95
            $this->triggerPreChanges($e);
96
97
            $this->toIdentityMap($entity);
98
99
            $this->triggerChanges($e);
100
            $this->triggerAttributesChange($e);
101
102
            return 1;
103
        } else {
104
            if (empty($id)) {
105
                $id = $this->primaryKey++;
106
                $entity->populate(compact('id'));
107
            }
108
109
            $this->toIdentityMap($entity);
110
111
            $this->triggerCreate($entity);
112
        }
113
114
        return $entity;
115
    }
116
117
    /**
118
     * @param EntityInterface $entity
119
     * @return int|null
120
     */
121
    public function remove(EntityInterface $entity)
122
    {
123
        $id = $entity->getId();
124
125
        if ($this->identityMap->offsetExists((int)$id)) {
126
            $this->identityMap->offsetUnset($id);
127
            $result = 1;
128
        } else {
129
            $result = 0;
130
        }
131
132
        $this->triggerDelete($entity);
133
134
        return $result;
135
    }
136
137
    /**
138
     * @param CriteriaInterface|array $criteria
139
     * @return EntityInterface|null
140
     */
141
    public function find($criteria)
142
    {
143
        if (is_array($criteria)) {
144
            $criteria = $this->createCriteria($criteria);
145
        }
146
147
        $callbacks = $criteria->getQuery();
148
149
        $result = null;
150
        /** @var EntityInterface $entity */
151 View Code Duplication
        foreach ($this->identityMap as $entity) {
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...
152
153
            $data = $entity->extract();
154
            $isSatisfied = true;
155
156
            foreach ($callbacks as $callback) {
157
                if (!$callback($data)) {
158
                    $isSatisfied = false;
159
                    break 1;
160
                }
161
            }
162
163
            if ($isSatisfied) {
164
                $result = $entity;
165
                break;
166
            }
167
        }
168
169
        if (is_null($result)) {
170
            return;
171
        }
172
173
        $entity = $result;
174
175
        $this->toIdentityMap($entity);
176
177
        return $entity;
178
    }
179
180
    /**
181
     * @param mixed $id
182
     * @return EntityInterface|null
183
     */
184
    public function findById($id)
185
    {
186
        $criteria = $this->createCriteria();
187
        $criteria->equalTo('id', $id);
188
189
        return $this->find($criteria);
190
    }
191
192
    /**
193
     * @param CriteriaInterface|array $criteria
194
     * @return EntityInterface[]
195
     */
196
    public function findMany($criteria)
197
    {
198
        if (is_array($criteria)) {
199
            $criteria = $this->createCriteria($criteria);
200
        }
201
202
        $callbacks = $criteria->getQuery();
203
204
        $entities = new $this->collectionClass;
205
206
        /** @var EntityInterface $entity */
207 View Code Duplication
        foreach ($this->identityMap as $entity) {
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
209
            $data = $entity->extract();
210
            $isSatisfied = true;
211
212
            foreach ($callbacks as $callback) {
213
                if (!$callback($data)) {
214
                    $isSatisfied = false;
215
                    break 1;
216
                }
217
            }
218
219
            if ($isSatisfied) {
220
                $entities->offsetSet($entity->getId(), $entity);
221
            }
222
        }
223
224
        foreach ($entities as $entity) {
225
            $this->toIdentityMap($entity);
226
        }
227
228
        return $entities;
229
    }
230
231
    /**
232
     * @param CriteriaInterface|array $criteria
233
     * @return int
234
     */
235
    public function count($criteria)
236
    {
237
        if (is_array($criteria)) {
238
            $criteria = $this->createCriteria($criteria);
239
        }
240
241
        /** @var ArrayObject $entities */
242
        $entities = $this->findMany($criteria);
243
244
        return $entities->count();
245
    }
246
247
    /**
248
     * @param array $filter
249
     * @return CriteriaInterface
250
     */
251
    public function createCriteria(array $filter = [])
252
    {
253
        return $this->criteriaFactory->buildInMemory($this->entityName, $filter);
254
    }
255
256
    /**
257
     * @param EntityInterface $entity
258
     */
259
    protected function toIdentityMap(EntityInterface $entity)
260
    {
261
        $this->identityMap->offsetSet($entity->getId(), $entity);
262
        $this->identityMapOriginal->offsetSet($entity->getId(), clone $entity);
263
    }
264
265
    /**
266
     * @param EntityInterface $changedEntity
267
     * @return bool
268
     */
269
    protected function isEntityChanged(EntityInterface $changedEntity)
270
    {
271
        $originalEntity = $this->identityMapOriginal->offsetGet($changedEntity->getId());
272
        return $changedEntity != $originalEntity;
273
    }
274
275
    /**
276
     * @return EntityChangedEvent
277
     */
278 View Code Duplication
    protected function getEvent()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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...
279
    {
280
        if (null === $this->event) {
281
            $this->event = new EntityChangedEvent();
282
            $this->event->setTarget($this);
283
        }
284
285
        return $this->event;
286
    }
287
288
    /**
289
     * @param EntityInterface $createdEntity
290
     */
291 View Code Duplication
    protected function triggerCreate(EntityInterface &$createdEntity)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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...
292
    {
293
        $this->eventManager->addIdentifiers(get_class($createdEntity));
294
295
        $event = new Event(
296
            sprintf('entity:%s:created', get_class($createdEntity)),
297
            $this,
298
            ['entity' => $createdEntity]
299
        );
300
        $this->eventManager->trigger($event);
301
302
        if ($event->getParam('entity') && $event->getParam('entity') instanceof EntityInterface) {
303
            $createdEntity = $event->getParam('entity');
304
        }
305
    }
306
307
    /**
308
     * @param EntityInterface $deletedEntity
309
     */
310 View Code Duplication
    protected function triggerDelete(EntityInterface $deletedEntity)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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...
311
    {
312
        $this->eventManager->addIdentifiers(get_class($deletedEntity));
313
314
        $event = new Event(
315
            sprintf('entity:%s:deleted', get_class($deletedEntity)),
316
            $this,
317
            ['entity' => $deletedEntity]
318
        );
319
        $this->eventManager->trigger($event);
320
    }
321
322
    /**
323
     * @param EntityChangedEvent $e
324
     */
325
    protected function triggerChanges(EntityChangedEvent $e)
326
    {
327
        $changedEntity = $e->getChangedEntity();
328
        $this->eventManager->trigger($this->getEntityChangeEventName($changedEntity), $this, $e);
329
    }
330
331
    /**
332
     * @param EntityChangedEvent $e
333
     */
334
    protected function triggerPreChanges(EntityChangedEvent $e)
335
    {
336
        $changedEntity = $e->getChangedEntity();
337
        $this->eventManager->trigger($this->getEntityChangeEventName($changedEntity).':pre', $this, $e);
338
    }
339
340
    /**
341
     * @param EntityChangedEvent $e
342
     */
343 View Code Duplication
    protected function triggerAttributesChange(EntityChangedEvent $e)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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...
344
    {
345
        $changedEntity = $e->getChangedEntity();
346
347
        $originalAttrs = $e->getOriginalEntity()->extract();
348
        $changedAttrs = $changedEntity->extract();
349
350
        foreach (array_keys(array_diff_assoc($originalAttrs, $changedAttrs)) as $attribute) {
351
            $this->eventManager->trigger($this->getAttributeChangeEventName($changedEntity, $attribute), $this, $e);
352
        }
353
    }
354
355
    /**
356
     * @param EntityInterface $changedEntity
357
     * @return string
358
     */
359
    protected function getEntityChangeEventName(EntityInterface $changedEntity)
360
    {
361
        return sprintf('entity:%s:changed', get_class($changedEntity));
362
    }
363
364
    /**
365
     * @param EntityInterface $changedEntity
366
     * @param $attributeName
367
     * @return string
368
     */
369
    protected function getAttributeChangeEventName(EntityInterface $changedEntity, $attributeName)
370
    {
371
        return sprintf('attribute:%s:%s:changed', get_class($changedEntity), $attributeName);
372
    }
373
}
374