Completed
Push — master ( e9e666...e79deb )
by Roni
01:22
created

DoctrineSubscriber::setDispatcher()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 4
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 1
1
<?php
2
3
/*
4
 * This file is part of the XiideaEasyAuditBundle package.
5
 *
6
 * (c) Xiidea <http://www.xiidea.net>
7
 *
8
 * This source file is subject to the MIT license that is bundled
9
 * with this source code in the file LICENSE.
10
 */
11
12
namespace Xiidea\EasyAuditBundle\Subscriber;
13
14
use Doctrine\Common\EventSubscriber;
15
use Doctrine\Common\Util\ClassUtils;
16
use Doctrine\ORM\Event\LifecycleEventArgs;
17
use Symfony\Component\DependencyInjection\ContainerAwareTrait;
18
use Symfony\Component\EventDispatcher\EventDispatcher;
19
use Xiidea\EasyAuditBundle\Events\DoctrineEntityEvent;
20
use Xiidea\EasyAuditBundle\Events\DoctrineEvents;
21
22
class DoctrineSubscriber implements EventSubscriber
23
{
24
    use ContainerAwareTrait;
25
26
    /** @var \Doctrine\Common\Annotations\Reader */
27
    private $annotationReader;
28
29
    private $toBeDeleted = [];
30
31
    /**
32
     * @var EventDispatcher
33
     */
34
    private $dispatcher;
35
36
    /**
37
     * @var array
38
     */
39
    private $entities;
40
41
    public function __construct($entities = array())
42
    {
43
        $this->entities = $entities;
44
    }
45
46
    public function getSubscribedEvents()
47
    {
48
        return array(
49
            'postPersist',
50
            'postUpdate',
51
            'preRemove',
52
            'postRemove'
53
        );
54
    }
55
56
    public function postPersist(LifecycleEventArgs $args)
57
    {
58
        $this->handleEvent(DoctrineEvents::ENTITY_CREATED, $args);
59
    }
60
61
    public function postUpdate(LifecycleEventArgs $args)
62
    {
63
        $this->handleEvent(DoctrineEvents::ENTITY_UPDATED, $args);
64
    }
65
66
    public function preRemove(LifecycleEventArgs $args)
67
    {
68
        if (false === $this->isConfiguredToTrack($args->getEntity(), DoctrineEvents::ENTITY_DELETED)) {
69
            return;
70
        }
71
72
        $className = ClassUtils::getClass($args->getEntity());
73
74
        if (!isset($this->toBeDeleted[$className])) {
75
            $this->toBeDeleted[$className] = [];
76
        }
77
78
        $this->toBeDeleted[$className][spl_object_hash($args->getEntity())] = $this->getIdentity($args, $className);
79
    }
80
81
    public function postRemove(LifecycleEventArgs $args)
82
    {
83
        $identity = $this->getToBeDeletedId($args->getEntity());
84
85
        if (null !== $identity) {
86
            $this->dispatcher->dispatch(DoctrineEvents::ENTITY_DELETED,
87
                new DoctrineEntityEvent($args, $identity)
88
            );
89
        }
90
    }
91
92
    private function getToBeDeletedId($entity)
93
    {
94
        if($this->isScheduledForDelete($entity)) {
95
            return $this->toBeDeleted[ClassUtils::getClass($entity)][spl_object_hash($entity)];
96
        }
97
98
        return null;
99
    }
100
101
    /**
102
     * @param string $eventName
103
     * @param LifecycleEventArgs $args
104
     */
105
    private function handleEvent($eventName, LifecycleEventArgs $args)
106
    {
107
        if (true === $this->isConfiguredToTrack($args->getEntity(), $eventName)) {
108
            $this->dispatcher->dispatch($eventName,
109
                new DoctrineEntityEvent($args, $this->getIdentity($args, ClassUtils::getClass($args->getEntity())))
110
            );
111
        }
112
    }
113
114
    /**
115
     * @param $entity
116
     * @param string $eventName
117
     * @return bool
118
     */
119
    private function isConfiguredToTrack($entity, $eventName = '')
120
    {
121
        $class = ClassUtils::getClass($entity);
122
        $eventType = DoctrineEvents::getShortEventType($eventName);
123
124
        if (null !== $track = $this->isAnnotatedEvent($entity, $eventType)) {
125
            return $track;
126
        }
127
128
        if (!$this->isConfigured($class)) {
129
            return FALSE;
130
        }
131
132
        if ($this->shouldTrackAllEventType($class)) {
133
            return TRUE;
134
        }
135
136
        return $this->shouldTrackEventType($eventType, $class);
137
    }
138
139
    /**
140
     * @param $entity
141
     * @param string $eventType
142
     * @return bool|null
143
     */
144
    protected function isAnnotatedEvent($entity, $eventType)
145
    {
146
        $metaData = $this->hasAnnotation($entity);
147
148
        if (!$metaData) {
149
            return null;
150
        }
151
152
        return empty($metaData->events) || in_array($eventType, $metaData->events);
153
    }
154
155
    /**
156
     * @param $entity
157
     * @return null|object
158
     */
159
    protected function hasAnnotation($entity)
160
    {
161
        $reflection = $this->getReflectionClassFromObject($entity);
162
163
        return $this
164
            ->getAnnotationReader()
165
            ->getClassAnnotation($reflection, 'Xiidea\EasyAuditBundle\Annotation\ORMSubscribedEvents');
166
167
    }
168
169
    /**
170
     * @return \Doctrine\Common\Annotations\Reader
171
     */
172
    protected function getAnnotationReader()
173
    {
174
        return $this->annotationReader;
175
    }
176
177
    /**
178
     * @param $object
179
     * @return \ReflectionClass
180
     */
181
    protected function getReflectionClassFromObject($object)
182
    {
183
        $class = ClassUtils::getClass($object);
184
185
        return new \ReflectionClass($class);
186
    }
187
188
    /**
189
     * @param string $eventType
190
     * @param string $class
191
     * @return bool
192
     */
193
    private function shouldTrackEventType($eventType, $class)
194
    {
195
        return (is_array($this->entities[$class]) && in_array($eventType, $this->entities[$class]));
196
    }
197
198
    /**
199
     * @param string $class
200
     * @return bool
201
     */
202
    private function shouldTrackAllEventType($class)
203
    {
204
        return empty($this->entities[$class]);
205
    }
206
207
    /**
208
     * @param string $class
209
     * @return bool
210
     */
211
    protected function isConfigured($class)
212
    {
213
        return isset($this->entities[$class]);
214
    }
215
216
    /**
217
     * @param \Doctrine\Common\Annotations\Reader $annotationReader
218
     */
219
    public function setAnnotationReader($annotationReader = null)
220
    {
221
        $this->annotationReader = $annotationReader;
222
    }
223
224
    /**
225
     * @param LifecycleEventArgs $args
226
     * @param $className
227
     * @return array
228
     */
229
    protected function getIdentity(LifecycleEventArgs $args, $className)
230
    {
231
        return $args->getEntityManager()->getClassMetadata($className)->getIdentifierValues($args->getEntity());
232
    }
233
234
    /**
235
     * @param $entity
236
     * @return boolean
237
     */
238
    private function isScheduledForDelete($entity)
239
    {
240
        $originalClassName = ClassUtils::getClass($entity);
241
242
        return isset($this->toBeDeleted[$originalClassName]) && isset($this->toBeDeleted[$originalClassName][spl_object_hash($entity)]);
243
    }
244
245
    /**
246
     * @param EventDispatcher $dispatcher
247
     */
248
    public function setDispatcher($dispatcher)
249
    {
250
        $this->dispatcher = $dispatcher;
251
    }
252
}
253