Passed
Push — master ( 4f15cf...4a5132 )
by Damien
02:50
created

AuditManager   A

Complexity

Total Complexity 31

Size/Duplication

Total Lines 339
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
eloc 101
dl 0
loc 339
rs 9.92
c 0
b 0
f 0
wmc 31

19 Methods

Rating   Name   Duplication   Size   Complexity  
A getConfiguration() 0 3 1
A __construct() 0 4 1
A processInsertions() 0 8 2
A associate() 0 3 1
A audit() 0 23 2
A setHelper() 0 3 1
A processAssociations() 0 5 2
A processUpdates() 0 8 2
A getHelper() 0 3 1
A update() 0 16 3
A selectStorageSpace() 0 3 1
A notify() 0 3 1
A dissociate() 0 3 1
A associateOrDissociate() 0 22 3
A processDissociations() 0 5 2
A process() 0 7 1
A insert() 0 12 2
A processDeletions() 0 5 2
A remove() 0 12 2
1
<?php
2
3
namespace DH\DoctrineAuditBundle\Manager;
4
5
use DH\DoctrineAuditBundle\AuditConfiguration;
6
use DH\DoctrineAuditBundle\Event\LifecycleEvent;
7
use DH\DoctrineAuditBundle\Helper\AuditHelper;
8
use Doctrine\ORM\EntityManagerInterface;
9
use Doctrine\ORM\Mapping\ClassMetadata;
10
11
class AuditManager
12
{
13
    /**
14
     * @var \DH\DoctrineAuditBundle\AuditConfiguration
15
     */
16
    private $configuration;
17
18
    /**
19
     * @var AuditHelper
20
     */
21
    private $helper;
22
23
    public function __construct(AuditConfiguration $configuration, AuditHelper $helper)
24
    {
25
        $this->configuration = $configuration;
26
        $this->helper = $helper;
27
    }
28
29
    /**
30
     * @return \DH\DoctrineAuditBundle\AuditConfiguration
31
     */
32
    public function getConfiguration(): AuditConfiguration
33
    {
34
        return $this->configuration;
35
    }
36
37
    /**
38
     * @param array $payload
39
     */
40
    public function notify(array $payload): void
41
    {
42
        $this->configuration->getEventDispatcher()->dispatch(new LifecycleEvent($payload));
43
    }
44
45
    /**
46
     * @param AuditTransaction $transaction
47
     *
48
     * @throws \Doctrine\DBAL\DBALException
49
     * @throws \Doctrine\ORM\Mapping\MappingException
50
     */
51
    public function process(AuditTransaction $transaction): void
52
    {
53
        $this->processInsertions($transaction);
54
        $this->processUpdates($transaction);
55
        $this->processAssociations($transaction);
56
        $this->processDissociations($transaction);
57
        $this->processDeletions($transaction);
58
    }
59
60
    /**
61
     * Adds an insert entry to the audit table.
62
     *
63
     * @param EntityManagerInterface $em
64
     * @param object                 $entity
65
     * @param array                  $ch
66
     * @param string                 $transactionHash
67
     *
68
     * @throws \Doctrine\DBAL\DBALException
69
     * @throws \Doctrine\ORM\Mapping\MappingException
70
     */
71
    public function insert(EntityManagerInterface $em, $entity, array $ch, string $transactionHash): void
72
    {
73
        $meta = $em->getClassMetadata(\get_class($entity));
74
        $this->audit($em, [
75
            'action' => 'insert',
76
            'blame' => $this->helper->blame(),
77
            'diff' => $this->helper->diff($em, $entity, $ch),
78
            'table' => $meta->getTableName(),
79
            'schema' => $meta->getSchemaName(),
80
            'id' => $this->helper->id($em, $entity),
81
            'transaction_hash' => $transactionHash,
82
            'discriminator' => ClassMetadata::INHERITANCE_TYPE_SINGLE_TABLE === $meta->inheritanceType ? \get_class($entity) : null,
0 ignored issues
show
Bug introduced by
Accessing inheritanceType on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
83
        ]);
84
    }
85
86
    /**
87
     * Adds an update entry to the audit table.
88
     *
89
     * @param EntityManagerInterface $em
90
     * @param object                 $entity
91
     * @param array                  $ch
92
     * @param string                 $transactionHash
93
     *
94
     * @throws \Doctrine\DBAL\DBALException
95
     * @throws \Doctrine\ORM\Mapping\MappingException
96
     */
97
    public function update(EntityManagerInterface $em, $entity, array $ch, string $transactionHash): void
98
    {
99
        $diff = $this->helper->diff($em, $entity, $ch);
100
        if (0 === \count($diff)) {
101
            return; // if there is no entity diff, do not log it
102
        }
103
        $meta = $em->getClassMetadata(\get_class($entity));
104
        $this->audit($em, [
105
            'action' => 'update',
106
            'blame' => $this->helper->blame(),
107
            'diff' => $diff,
108
            'table' => $meta->getTableName(),
109
            'schema' => $meta->getSchemaName(),
110
            'id' => $this->helper->id($em, $entity),
111
            'transaction_hash' => $transactionHash,
112
            'discriminator' => ClassMetadata::INHERITANCE_TYPE_SINGLE_TABLE === $meta->inheritanceType ? \get_class($entity) : null,
0 ignored issues
show
Bug introduced by
Accessing inheritanceType on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
113
        ]);
114
    }
115
116
    /**
117
     * Adds a remove entry to the audit table.
118
     *
119
     * @param EntityManagerInterface $em
120
     * @param object                 $entity
121
     * @param mixed                  $id
122
     * @param string                 $transactionHash
123
     *
124
     * @throws \Doctrine\DBAL\DBALException
125
     * @throws \Doctrine\ORM\Mapping\MappingException
126
     */
127
    public function remove(EntityManagerInterface $em, $entity, $id, string $transactionHash): void
128
    {
129
        $meta = $em->getClassMetadata(\get_class($entity));
130
        $this->audit($em, [
131
            'action' => 'remove',
132
            'blame' => $this->helper->blame(),
133
            'diff' => $this->helper->summarize($em, $entity, $id),
134
            'table' => $meta->getTableName(),
135
            'schema' => $meta->getSchemaName(),
136
            'id' => $id,
137
            'transaction_hash' => $transactionHash,
138
            'discriminator' => ClassMetadata::INHERITANCE_TYPE_SINGLE_TABLE === $meta->inheritanceType ? \get_class($entity) : null,
0 ignored issues
show
Bug introduced by
Accessing inheritanceType on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
139
        ]);
140
    }
141
142
    /**
143
     * Adds an association entry to the audit table.
144
     *
145
     * @param EntityManagerInterface $em
146
     * @param object                 $source
147
     * @param object                 $target
148
     * @param array                  $mapping
149
     * @param string                 $transactionHash
150
     *
151
     * @throws \Doctrine\DBAL\DBALException
152
     * @throws \Doctrine\ORM\Mapping\MappingException
153
     */
154
    public function associate(EntityManagerInterface $em, $source, $target, array $mapping, string $transactionHash): void
155
    {
156
        $this->associateOrDissociate('associate', $em, $source, $target, $mapping, $transactionHash);
157
    }
158
159
    /**
160
     * Adds a dissociation entry to the audit table.
161
     *
162
     * @param EntityManagerInterface $em
163
     * @param object                 $source
164
     * @param object                 $target
165
     * @param array                  $mapping
166
     * @param string                 $transactionHash
167
     *
168
     * @throws \Doctrine\DBAL\DBALException
169
     * @throws \Doctrine\ORM\Mapping\MappingException
170
     */
171
    public function dissociate(EntityManagerInterface $em, $source, $target, array $mapping, string $transactionHash): void
172
    {
173
        $this->associateOrDissociate('dissociate', $em, $source, $target, $mapping, $transactionHash);
174
    }
175
176
    /**
177
     * Adds an association entry to the audit table.
178
     *
179
     * @param string                 $type
180
     * @param EntityManagerInterface $em
181
     * @param object                 $source
182
     * @param object                 $target
183
     * @param array                  $mapping
184
     * @param string                 $transactionHash
185
     *
186
     * @throws \Doctrine\DBAL\DBALException
187
     * @throws \Doctrine\ORM\Mapping\MappingException
188
     */
189
    private function associateOrDissociate(string $type, EntityManagerInterface $em, $source, $target, array $mapping, string $transactionHash): void
190
    {
191
        $meta = $em->getClassMetadata(\get_class($source));
192
        $data = [
193
            'action' => $type,
194
            'blame' => $this->helper->blame(),
195
            'diff' => [
196
                'source' => $this->helper->summarize($em, $source),
197
                'target' => $this->helper->summarize($em, $target),
198
            ],
199
            'table' => $meta->getTableName(),
200
            'schema' => $meta->getSchemaName(),
201
            'id' => $this->helper->id($em, $source),
202
            'transaction_hash' => $transactionHash,
203
            'discriminator' => ClassMetadata::INHERITANCE_TYPE_SINGLE_TABLE === $meta->inheritanceType ? \get_class($source) : null,
0 ignored issues
show
Bug introduced by
Accessing inheritanceType on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
204
        ];
205
206
        if (isset($mapping['joinTable']['name'])) {
207
            $data['diff']['table'] = $mapping['joinTable']['name'];
208
        }
209
210
        $this->audit($em, $data);
211
    }
212
213
    /**
214
     * Adds an entry to the audit table.
215
     *
216
     * @param EntityManagerInterface $em
217
     * @param array                  $data
218
     *
219
     * @throws \Exception
220
     */
221
    private function audit(EntityManagerInterface $em, array $data): void
222
    {
223
        $schema = $data['schema'] ? $data['schema'].'.' : '';
224
        $auditTable = $schema.$this->configuration->getTablePrefix().$data['table'].$this->configuration->getTableSuffix();
225
        $dt = new \DateTime('now', new \DateTimeZone($this->getConfiguration()->getTimezone()));
226
227
        $payload = [
228
            'table' => $auditTable,
229
            'type' => $data['action'],
230
            'object_id' => (string) $data['id'],
231
            'discriminator' => $data['discriminator'],
232
            'transaction_hash' => (string) $data['transaction_hash'],
233
            'diffs' => json_encode($data['diff']),
234
            'blame_id' => $data['blame']['user_id'],
235
            'blame_user' => $data['blame']['username'],
236
            'blame_user_fqdn' => $data['blame']['user_fqdn'],
237
            'blame_user_firewall' => $data['blame']['user_firewall'],
238
            'ip' => $data['blame']['client_ip'],
239
            'created_at' => $dt->format('Y-m-d H:i:s'),
240
        ];
241
242
        // send an `AuditEvent` event
243
        $this->notify($payload);
244
    }
245
246
    /**
247
     * Set the value of helper.
248
     *
249
     * @param AuditHelper $helper
250
     */
251
    public function setHelper(AuditHelper $helper): void
252
    {
253
        $this->helper = $helper;
254
    }
255
256
    /**
257
     * Get the value of helper.
258
     *
259
     * @return AuditHelper
260
     */
261
    public function getHelper(): AuditHelper
262
    {
263
        return $this->helper;
264
    }
265
266
    /**
267
     * @param AuditTransaction $transaction
268
     *
269
     * @throws \Doctrine\DBAL\DBALException
270
     * @throws \Doctrine\ORM\Mapping\MappingException
271
     */
272
    public function processInsertions(AuditTransaction $transaction): void
273
    {
274
        $em = $transaction->getEntityManager();
275
        $uow = $em->getUnitOfWork();
276
        foreach ($transaction->getInserted() as list($entity, $ch)) {
277
            // the changeset might be updated from UOW extra updates
278
            $ch = array_merge($ch, $uow->getEntityChangeSet($entity));
279
            $this->insert($em, $entity, $ch, $transaction->getTransactionHash());
280
        }
281
    }
282
283
    /**
284
     * @param AuditTransaction $transaction
285
     *
286
     * @throws \Doctrine\DBAL\DBALException
287
     * @throws \Doctrine\ORM\Mapping\MappingException
288
     */
289
    public function processUpdates(AuditTransaction $transaction): void
290
    {
291
        $em = $transaction->getEntityManager();
292
        $uow = $em->getUnitOfWork();
293
        foreach ($transaction->getUpdated() as list($entity, $ch)) {
294
            // the changeset might be updated from UOW extra updates
295
            $ch = array_merge($ch, $uow->getEntityChangeSet($entity));
296
            $this->update($em, $entity, $ch, $transaction->getTransactionHash());
297
        }
298
    }
299
300
    /**
301
     * @param AuditTransaction $transaction
302
     *
303
     * @throws \Doctrine\DBAL\DBALException
304
     * @throws \Doctrine\ORM\Mapping\MappingException
305
     */
306
    public function processAssociations(AuditTransaction $transaction): void
307
    {
308
        $em = $transaction->getEntityManager();
309
        foreach ($transaction->getAssociated() as list($source, $target, $mapping)) {
310
            $this->associate($em, $source, $target, $mapping, $transaction->getTransactionHash());
311
        }
312
    }
313
314
    /**
315
     * @param AuditTransaction $transaction
316
     *
317
     * @throws \Doctrine\DBAL\DBALException
318
     * @throws \Doctrine\ORM\Mapping\MappingException
319
     */
320
    public function processDissociations(AuditTransaction $transaction): void
321
    {
322
        $em = $transaction->getEntityManager();
323
        foreach ($transaction->getDissociated() as list($source, $target, $id, $mapping)) {
324
            $this->dissociate($em, $source, $target, $mapping, $transaction->getTransactionHash());
325
        }
326
    }
327
328
    /**
329
     * @param AuditTransaction $transaction
330
     *
331
     * @throws \Doctrine\DBAL\DBALException
332
     * @throws \Doctrine\ORM\Mapping\MappingException
333
     */
334
    public function processDeletions(AuditTransaction $transaction): void
335
    {
336
        $em = $transaction->getEntityManager();
337
        foreach ($transaction->getRemoved() as list($entity, $id)) {
338
            $this->remove($em, $entity, $id, $transaction->getTransactionHash());
339
        }
340
    }
341
342
    /**
343
     * @param EntityManagerInterface $em
344
     *
345
     * @return EntityManagerInterface
346
     */
347
    public function selectStorageSpace(EntityManagerInterface $em): EntityManagerInterface
348
    {
349
        return $this->configuration->getEntityManager() ?? $em;
350
    }
351
}
352