Passed
Pull Request — master (#102)
by Damien
02:36
created

AuditManager::processUpdates()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 8
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

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