Completed
Pull Request — master (#34)
by
unknown
13:06
created

DoctrineSubscriber::setAnnotationReader()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

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