Passed
Pull Request — master (#50)
by Damien
02:13
created

AuditManager::remove()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 10
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 8
nc 1
nop 3
dl 0
loc 10
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
     * Get the value of helper.
240
     *
241
     * @return AuditHelper
242
     */
243
    public function getHelper(): AuditHelper
244
    {
245
        return $this->helper;
246
    }
247
248
    /**
249
     * @param \Doctrine\ORM\UnitOfWork $uow
250
     */
251
    public function collectScheduledInsertions(\Doctrine\ORM\UnitOfWork $uow): void
252
    {
253
        foreach ($uow->getScheduledEntityInsertions() as $entity) {
254
            if ($this->configuration->isAudited($entity)) {
255
                $this->inserted[] = [
256
                    $entity,
257
                    $uow->getEntityChangeSet($entity),
258
                ];
259
            }
260
        }
261
    }
262
263
    /**
264
     * @param \Doctrine\ORM\UnitOfWork $uow
265
     */
266
    public function collectScheduledUpdates(\Doctrine\ORM\UnitOfWork $uow): void
267
    {
268
        foreach ($uow->getScheduledEntityUpdates() as $entity) {
269
            if ($this->configuration->isAudited($entity)) {
270
                $this->updated[] = [
271
                    $entity,
272
                    $uow->getEntityChangeSet($entity),
273
                ];
274
            }
275
        }
276
    }
277
278
    /**
279
     * @param \Doctrine\ORM\UnitOfWork $uow
280
     * @param EntityManager            $em
281
     *
282
     * @throws \Doctrine\DBAL\DBALException
283
     * @throws \Doctrine\ORM\Mapping\MappingException
284
     */
285
    public function collectScheduledDeletions(\Doctrine\ORM\UnitOfWork $uow, EntityManager $em): void
286
    {
287
        foreach ($uow->getScheduledEntityDeletions() as $entity) {
288
            if ($this->configuration->isAudited($entity)) {
289
                $uow->initializeObject($entity);
290
                $this->removed[] = [
291
                    $entity,
292
                    $this->helper->id($em, $entity),
293
                ];
294
            }
295
        }
296
    }
297
298
    /**
299
     * @param \Doctrine\ORM\UnitOfWork $uow
300
     * @param EntityManager            $em
301
     *
302
     * @throws \Doctrine\DBAL\DBALException
303
     * @throws \Doctrine\ORM\Mapping\MappingException
304
     */
305
    public function collectScheduledCollectionUpdates(\Doctrine\ORM\UnitOfWork $uow, EntityManager $em): void
306
    {
307
        foreach ($uow->getScheduledCollectionUpdates() as $collection) {
308
            if ($this->configuration->isAudited($collection->getOwner())) {
309
                $mapping = $collection->getMapping();
310
                foreach ($collection->getInsertDiff() as $entity) {
311
                    if ($this->configuration->isAudited($entity)) {
312
                        $this->associated[] = [
313
                            $collection->getOwner(),
314
                            $entity,
315
                            $mapping,
316
                        ];
317
                    }
318
                }
319
                foreach ($collection->getDeleteDiff() as $entity) {
320
                    if ($this->configuration->isAudited($entity)) {
321
                        $this->dissociated[] = [
322
                            $collection->getOwner(),
323
                            $entity,
324
                            $this->helper->id($em, $entity),
325
                            $mapping,
326
                        ];
327
                    }
328
                }
329
            }
330
        }
331
    }
332
333
    /**
334
     * @param \Doctrine\ORM\UnitOfWork $uow
335
     * @param EntityManager            $em
336
     *
337
     * @throws \Doctrine\DBAL\DBALException
338
     * @throws \Doctrine\ORM\Mapping\MappingException
339
     */
340
    public function collectScheduledCollectionDeletions(\Doctrine\ORM\UnitOfWork $uow, EntityManager $em): void
341
    {
342
        foreach ($uow->getScheduledCollectionDeletions() as $collection) {
343
            if ($this->configuration->isAudited($collection->getOwner())) {
344
                $mapping = $collection->getMapping();
345
                foreach ($collection->toArray() as $entity) {
346
                    if (!$this->configuration->isAudited($entity)) {
347
                        continue;
348
                    }
349
                    $this->dissociated[] = [
350
                        $collection->getOwner(),
351
                        $entity,
352
                        $this->helper->id($em, $entity),
353
                        $mapping,
354
                    ];
355
                }
356
            }
357
        }
358
    }
359
360
    /**
361
     * @param EntityManager            $em
362
     * @param \Doctrine\ORM\UnitOfWork $uow
363
     *
364
     * @throws \Doctrine\DBAL\DBALException
365
     * @throws \Doctrine\ORM\Mapping\MappingException
366
     */
367
    public function processInsertions(EntityManager $em, \Doctrine\ORM\UnitOfWork $uow): void
368
    {
369
        foreach ($this->inserted as list($entity, $ch)) {
370
            // the changeset might be updated from UOW extra updates
371
            $ch = array_merge($ch, $uow->getEntityChangeSet($entity));
372
            $this->insert($em, $entity, $ch);
373
        }
374
    }
375
376
    /**
377
     * @param EntityManager            $em
378
     * @param \Doctrine\ORM\UnitOfWork $uow
379
     *
380
     * @throws \Doctrine\DBAL\DBALException
381
     * @throws \Doctrine\ORM\Mapping\MappingException
382
     */
383
    public function processUpdates(EntityManager $em, \Doctrine\ORM\UnitOfWork $uow): void
384
    {
385
        foreach ($this->updated as list($entity, $ch)) {
386
            // the changeset might be updated from UOW extra updates
387
            $ch = array_merge($ch, $uow->getEntityChangeSet($entity));
388
            $this->update($em, $entity, $ch);
389
        }
390
    }
391
392
    /**
393
     * @param EntityManager $em
394
     *
395
     * @throws \Doctrine\DBAL\DBALException
396
     * @throws \Doctrine\ORM\Mapping\MappingException
397
     */
398
    public function processAssociations(EntityManager $em): void
399
    {
400
        foreach ($this->associated as list($source, $target, $mapping)) {
401
            $this->associate($em, $source, $target, $mapping);
402
        }
403
    }
404
405
    /**
406
     * @param EntityManager $em
407
     *
408
     * @throws \Doctrine\DBAL\DBALException
409
     * @throws \Doctrine\ORM\Mapping\MappingException
410
     */
411
    public function processDissociations(EntityManager $em): void
412
    {
413
        foreach ($this->dissociated as list($source, $target, $id, $mapping)) {
414
            $this->dissociate($em, $source, $target, $mapping);
415
        }
416
    }
417
418
    /**
419
     * @param EntityManager $em
420
     *
421
     * @throws \Doctrine\DBAL\DBALException
422
     * @throws \Doctrine\ORM\Mapping\MappingException
423
     */
424
    public function processDeletions(EntityManager $em): void
425
    {
426
        foreach ($this->removed as list($entity, $id)) {
427
            $this->remove($em, $entity, $id);
428
        }
429
    }
430
431
    public function reset(): void
432
    {
433
        $this->inserted = [];
434
        $this->updated = [];
435
        $this->removed = [];
436
        $this->associated = [];
437
        $this->dissociated = [];
438
    }
439
}
440