Completed
Push — master ( 665254...e5147d )
by max
02:16
created

Repository   A

Complexity

Total Complexity 32

Size/Duplication

Total Lines 365
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 11

Importance

Changes 0
Metric Value
wmc 32
lcom 1
cbo 11
dl 0
loc 365
rs 9.6
c 0
b 0
f 0

18 Methods

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