Passed
Pull Request — master (#51)
by Damien
02:10
created

AuditManager::setHelper()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 1
dl 0
loc 3
rs 10
c 0
b 0
f 0
1
<?php
2
3
namespace DH\DoctrineAuditBundle;
4
5
use DH\DoctrineAuditBundle\Helper\AuditHelper;
6
use Doctrine\ORM\EntityManager;
7
8
class AuditManager
9
{
10
    /**
11
     * @var \DH\DoctrineAuditBundle\AuditConfiguration
12
     */
13
    private $configuration;
14
15
    private $inserted = [];     // [$source, $changeset]
16
    private $updated = [];      // [$source, $changeset]
17
    private $removed = [];      // [$source, $id]
18
    private $associated = [];   // [$source, $target, $mapping]
19
    private $dissociated = [];  // [$source, $target, $id, $mapping]
20
21
    /**
22
     * @var AuditHelper
23
     */
24
    private $helper;
25
26
    public function __construct(AuditConfiguration $configuration, AuditHelper $helper)
27
    {
28
        $this->configuration = $configuration;
29
        $this->helper = $helper;
30
    }
31
32
    /**
33
     * @return \DH\DoctrineAuditBundle\AuditConfiguration
34
     */
35
    public function getConfiguration(): AuditConfiguration
36
    {
37
        return $this->configuration;
38
    }
39
40
    /**
41
     * Adds an insert entry to the audit table.
42
     *
43
     * @param EntityManager $em
44
     * @param object        $entity
45
     * @param array         $ch
46
     *
47
     * @throws \Doctrine\DBAL\DBALException
48
     * @throws \Doctrine\ORM\Mapping\MappingException
49
     */
50
    public function insert(EntityManager $em, $entity, array $ch): void
51
    {
52
        $meta = $em->getClassMetadata(\get_class($entity));
53
        $this->audit($em, [
54
            'action' => 'insert',
55
            'blame' => $this->helper->blame(),
56
            'diff' => $this->helper->diff($em, $entity, $ch),
57
            'table' => $meta->table['name'],
58
            'schema' => $meta->table['schema'] ?? null,
59
            'id' => $this->helper->id($em, $entity),
60
        ]);
61
    }
62
63
    /**
64
     * Adds an update entry to the audit table.
65
     *
66
     * @param EntityManager $em
67
     * @param object        $entity
68
     * @param array         $ch
69
     *
70
     * @throws \Doctrine\DBAL\DBALException
71
     * @throws \Doctrine\ORM\Mapping\MappingException
72
     */
73
    public function update(EntityManager $em, $entity, array $ch): void
74
    {
75
        $diff = $this->helper->diff($em, $entity, $ch);
76
        if (!$diff) {
77
            return; // if there is no entity diff, do not log it
78
        }
79
        $meta = $em->getClassMetadata(\get_class($entity));
80
        $this->audit($em, [
81
            'action' => 'update',
82
            'blame' => $this->helper->blame(),
83
            'diff' => $diff,
84
            'table' => $meta->table['name'],
85
            'schema' => $meta->table['schema'] ?? null,
86
            'id' => $this->helper->id($em, $entity),
87
        ]);
88
    }
89
90
    /**
91
     * Adds a remove entry to the audit table.
92
     *
93
     * @param EntityManager $em
94
     * @param object        $entity
95
     * @param mixed         $id
96
     *
97
     * @throws \Doctrine\DBAL\DBALException
98
     * @throws \Doctrine\ORM\Mapping\MappingException
99
     */
100
    public function remove(EntityManager $em, $entity, $id): void
101
    {
102
        $meta = $em->getClassMetadata(\get_class($entity));
103
        $this->audit($em, [
104
            'action' => 'remove',
105
            'blame' => $this->helper->blame(),
106
            'diff' => $this->helper->summarize($em, $entity, $id),
107
            'table' => $meta->table['name'],
108
            'schema' => $meta->table['schema'] ?? null,
109
            'id' => $id,
110
        ]);
111
    }
112
113
    /**
114
     * Adds an association entry to the audit table.
115
     *
116
     * @param EntityManager $em
117
     * @param object        $source
118
     * @param object        $target
119
     * @param array         $mapping
120
     *
121
     * @throws \Doctrine\DBAL\DBALException
122
     * @throws \Doctrine\ORM\Mapping\MappingException
123
     */
124
    public function associate(EntityManager $em, $source, $target, array $mapping): void
125
    {
126
        $this->associateOrDissociate('associate', $em, $source, $target, $mapping);
127
    }
128
129
    /**
130
     * Adds a dissociation entry to the audit table.
131
     *
132
     * @param EntityManager $em
133
     * @param object        $source
134
     * @param object        $target
135
     * @param array         $mapping
136
     *
137
     * @throws \Doctrine\DBAL\DBALException
138
     * @throws \Doctrine\ORM\Mapping\MappingException
139
     */
140
    public function dissociate(EntityManager $em, $source, $target, array $mapping): void
141
    {
142
        $this->associateOrDissociate('dissociate', $em, $source, $target, $mapping);
143
    }
144
145
    /**
146
     * @param EntityManager $em
147
     * @param object        $entity
148
     *
149
     * @throws \Doctrine\DBAL\DBALException
150
     * @throws \Doctrine\ORM\Mapping\MappingException
151
     */
152
    public function softDelete(EntityManager $em, $entity): void
153
    {
154
        if ($this->configuration->isAudited($entity)) {
155
            $this->removed[] = [
156
                $entity,
157
                $this->helper->id($em, $entity),
158
            ];
159
        }
160
    }
161
162
    /**
163
     * Adds an association entry to the audit table.
164
     *
165
     * @param string        $type
166
     * @param EntityManager $em
167
     * @param object        $source
168
     * @param object        $target
169
     * @param array         $mapping
170
     *
171
     * @throws \Doctrine\DBAL\DBALException
172
     * @throws \Doctrine\ORM\Mapping\MappingException
173
     */
174
    private function associateOrDissociate(string $type, EntityManager $em, $source, $target, array $mapping): void
175
    {
176
        $meta = $em->getClassMetadata(\get_class($source));
177
        $data = [
178
            'action' => $type,
179
            'blame' => $this->helper->blame(),
180
            'diff' => [
181
                'source' => $this->helper->summarize($em, $source),
182
                'target' => $this->helper->summarize($em, $target),
183
            ],
184
            'table' => $meta->table['name'],
185
            'schema' => $meta->table['schema'] ?? null,
186
            'id' => $this->helper->id($em, $source),
187
        ];
188
189
        if (isset($mapping['joinTable']['name'])) {
190
            $data['diff']['table'] = $mapping['joinTable']['name'];
191
        }
192
193
        $this->audit($em, $data);
194
    }
195
196
    /**
197
     * Adds an entry to the audit table.
198
     *
199
     * @param EntityManager $em
200
     * @param array         $data
201
     *
202
     * @throws \Doctrine\DBAL\DBALException
203
     */
204
    private function audit(EntityManager $em, array $data): void
205
    {
206
        $schema = $data['schema'] ? $data['schema'].'.' : '';
207
        $auditTable = $schema.$this->configuration->getTablePrefix().$data['table'].$this->configuration->getTableSuffix();
208
        $fields = [
209
            'type' => ':type',
210
            'object_id' => ':object_id',
211
            'diffs' => ':diffs',
212
            'blame_id' => ':blame_id',
213
            'blame_user' => ':blame_user',
214
            'ip' => ':ip',
215
            'created_at' => ':created_at',
216
        ];
217
218
        $query = sprintf(
219
            'INSERT INTO %s (%s) VALUES (%s)',
220
            $auditTable,
221
            implode(', ', array_keys($fields)),
222
            implode(', ', array_values($fields))
223
        );
224
225
        $statement = $em->getConnection()->prepare($query);
226
227
        $dt = new \DateTime('now', new \DateTimeZone('UTC'));
228
        $statement->bindValue('type', $data['action']);
229
        $statement->bindValue('object_id', $data['id']);
230
        $statement->bindValue('diffs', json_encode($data['diff']));
231
        $statement->bindValue('blame_id', $data['blame']['user_id']);
232
        $statement->bindValue('blame_user', $data['blame']['username']);
233
        $statement->bindValue('ip', $data['blame']['client_ip']);
234
        $statement->bindValue('created_at', $dt->format('Y-m-d H:i:s'));
235
        $statement->execute();
236
    }
237
238
    /**
239
     * Set the value of helper.
240
     *
241
     * @param AuditHelper $helper
242
     */
243
    public function setHelper(AuditHelper $helper): void
244
    {
245
        $this->helper = $helper;
246
    }
247
248
    /**
249
     * Get the value of helper.
250
     *
251
     * @return AuditHelper
252
     */
253
    public function getHelper(): AuditHelper
254
    {
255
        return $this->helper;
256
    }
257
258
    /**
259
     * @param \Doctrine\ORM\UnitOfWork $uow
260
     */
261
    public function collectScheduledInsertions(\Doctrine\ORM\UnitOfWork $uow): void
262
    {
263
        foreach ($uow->getScheduledEntityInsertions() as $entity) {
264
            if ($this->configuration->isAudited($entity)) {
265
                $this->inserted[] = [
266
                    $entity,
267
                    $uow->getEntityChangeSet($entity),
268
                ];
269
            }
270
        }
271
    }
272
273
    /**
274
     * @param \Doctrine\ORM\UnitOfWork $uow
275
     */
276
    public function collectScheduledUpdates(\Doctrine\ORM\UnitOfWork $uow): void
277
    {
278
        foreach ($uow->getScheduledEntityUpdates() as $entity) {
279
            if ($this->configuration->isAudited($entity)) {
280
                $this->updated[] = [
281
                    $entity,
282
                    $uow->getEntityChangeSet($entity),
283
                ];
284
            }
285
        }
286
    }
287
288
    /**
289
     * @param \Doctrine\ORM\UnitOfWork $uow
290
     * @param EntityManager            $em
291
     *
292
     * @throws \Doctrine\DBAL\DBALException
293
     * @throws \Doctrine\ORM\Mapping\MappingException
294
     */
295
    public function collectScheduledDeletions(\Doctrine\ORM\UnitOfWork $uow, EntityManager $em): void
296
    {
297
        foreach ($uow->getScheduledEntityDeletions() as $entity) {
298
            if ($this->configuration->isAudited($entity)) {
299
                $uow->initializeObject($entity);
300
                $this->removed[] = [
301
                    $entity,
302
                    $this->helper->id($em, $entity),
303
                ];
304
            }
305
        }
306
    }
307
308
    /**
309
     * @param \Doctrine\ORM\UnitOfWork $uow
310
     * @param EntityManager            $em
311
     *
312
     * @throws \Doctrine\DBAL\DBALException
313
     * @throws \Doctrine\ORM\Mapping\MappingException
314
     */
315
    public function collectScheduledCollectionUpdates(\Doctrine\ORM\UnitOfWork $uow, EntityManager $em): void
316
    {
317
        foreach ($uow->getScheduledCollectionUpdates() as $collection) {
318
            if ($this->configuration->isAudited($collection->getOwner())) {
319
                $mapping = $collection->getMapping();
320
                foreach ($collection->getInsertDiff() as $entity) {
321
                    if ($this->configuration->isAudited($entity)) {
322
                        $this->associated[] = [
323
                            $collection->getOwner(),
324
                            $entity,
325
                            $mapping,
326
                        ];
327
                    }
328
                }
329
                foreach ($collection->getDeleteDiff() as $entity) {
330
                    if ($this->configuration->isAudited($entity)) {
331
                        $this->dissociated[] = [
332
                            $collection->getOwner(),
333
                            $entity,
334
                            $this->helper->id($em, $entity),
335
                            $mapping,
336
                        ];
337
                    }
338
                }
339
            }
340
        }
341
    }
342
343
    /**
344
     * @param \Doctrine\ORM\UnitOfWork $uow
345
     * @param EntityManager            $em
346
     *
347
     * @throws \Doctrine\DBAL\DBALException
348
     * @throws \Doctrine\ORM\Mapping\MappingException
349
     */
350
    public function collectScheduledCollectionDeletions(\Doctrine\ORM\UnitOfWork $uow, EntityManager $em): void
351
    {
352
        foreach ($uow->getScheduledCollectionDeletions() as $collection) {
353
            if ($this->configuration->isAudited($collection->getOwner())) {
354
                $mapping = $collection->getMapping();
355
                foreach ($collection->toArray() as $entity) {
356
                    if (!$this->configuration->isAudited($entity)) {
357
                        continue;
358
                    }
359
                    $this->dissociated[] = [
360
                        $collection->getOwner(),
361
                        $entity,
362
                        $this->helper->id($em, $entity),
363
                        $mapping,
364
                    ];
365
                }
366
            }
367
        }
368
    }
369
370
    /**
371
     * @param EntityManager            $em
372
     * @param \Doctrine\ORM\UnitOfWork $uow
373
     *
374
     * @throws \Doctrine\DBAL\DBALException
375
     * @throws \Doctrine\ORM\Mapping\MappingException
376
     */
377
    public function processInsertions(EntityManager $em, \Doctrine\ORM\UnitOfWork $uow): void
378
    {
379
        foreach ($this->inserted as list($entity, $ch)) {
380
            // the changeset might be updated from UOW extra updates
381
            $ch = array_merge($ch, $uow->getEntityChangeSet($entity));
382
            $this->insert($em, $entity, $ch);
383
        }
384
    }
385
386
    /**
387
     * @param EntityManager            $em
388
     * @param \Doctrine\ORM\UnitOfWork $uow
389
     *
390
     * @throws \Doctrine\DBAL\DBALException
391
     * @throws \Doctrine\ORM\Mapping\MappingException
392
     */
393
    public function processUpdates(EntityManager $em, \Doctrine\ORM\UnitOfWork $uow): void
394
    {
395
        foreach ($this->updated as list($entity, $ch)) {
396
            // the changeset might be updated from UOW extra updates
397
            $ch = array_merge($ch, $uow->getEntityChangeSet($entity));
398
            $this->update($em, $entity, $ch);
399
        }
400
    }
401
402
    /**
403
     * @param EntityManager $em
404
     *
405
     * @throws \Doctrine\DBAL\DBALException
406
     * @throws \Doctrine\ORM\Mapping\MappingException
407
     */
408
    public function processAssociations(EntityManager $em): void
409
    {
410
        foreach ($this->associated as list($source, $target, $mapping)) {
411
            $this->associate($em, $source, $target, $mapping);
412
        }
413
    }
414
415
    /**
416
     * @param EntityManager $em
417
     *
418
     * @throws \Doctrine\DBAL\DBALException
419
     * @throws \Doctrine\ORM\Mapping\MappingException
420
     */
421
    public function processDissociations(EntityManager $em): void
422
    {
423
        foreach ($this->dissociated as list($source, $target, $id, $mapping)) {
424
            $this->dissociate($em, $source, $target, $mapping);
425
        }
426
    }
427
428
    /**
429
     * @param EntityManager $em
430
     *
431
     * @throws \Doctrine\DBAL\DBALException
432
     * @throws \Doctrine\ORM\Mapping\MappingException
433
     */
434
    public function processDeletions(EntityManager $em): void
435
    {
436
        foreach ($this->removed as list($entity, $id)) {
437
            $this->remove($em, $entity, $id);
438
        }
439
    }
440
441
    public function reset(): void
442
    {
443
        $this->inserted = [];
444
        $this->updated = [];
445
        $this->removed = [];
446
        $this->associated = [];
447
        $this->dissociated = [];
448
    }
449
}
450