Passed
Pull Request — master (#82)
by Damien
03:02
created

AuditHelper::getAuditTableColumns()   B

Complexity

Conditions 1
Paths 1

Size

Total Lines 81
Code Lines 59

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 59
c 0
b 0
f 0
nc 1
nop 0
dl 0
loc 81
rs 8.8945

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
namespace DH\DoctrineAuditBundle\Helper;
4
5
use DH\DoctrineAuditBundle\AuditConfiguration;
6
use DH\DoctrineAuditBundle\User\UserInterface;
7
use Doctrine\DBAL\Types\Type;
8
use Doctrine\ORM\EntityManager;
9
10
class AuditHelper
11
{
12
    /**
13
     * @var \DH\DoctrineAuditBundle\AuditConfiguration
14
     */
15
    private $configuration;
16
17
    /**
18
     * @param AuditConfiguration $configuration
19
     */
20
    public function __construct(AuditConfiguration $configuration)
21
    {
22
        $this->configuration = $configuration;
23
    }
24
25
    /**
26
     * @return \DH\DoctrineAuditBundle\AuditConfiguration
27
     */
28
    public function getConfiguration(): AuditConfiguration
29
    {
30
        return $this->configuration;
31
    }
32
33
    /**
34
     * Returns the primary key value of an entity.
35
     *
36
     * @param EntityManager $em
37
     * @param object        $entity
38
     *
39
     * @throws \Doctrine\DBAL\DBALException
40
     * @throws \Doctrine\ORM\Mapping\MappingException
41
     *
42
     * @return mixed
43
     */
44
    public function id(EntityManager $em, $entity)
45
    {
46
        $meta = $em->getClassMetadata(\get_class($entity));
47
        $pk = $meta->getSingleIdentifierFieldName();
48
49
        if (isset($meta->fieldMappings[$pk])) {
50
            $type = Type::getType($meta->fieldMappings[$pk]['type']);
51
52
            return $this->value($em, $type, $meta->getReflectionProperty($pk)->getValue($entity));
53
        }
54
55
        /**
56
         * Primary key is not part of fieldMapping.
57
         *
58
         * @see https://github.com/DamienHarper/DoctrineAuditBundle/issues/40
59
         * @see https://www.doctrine-project.org/projects/doctrine-orm/en/latest/tutorials/composite-primary-keys.html#identity-through-foreign-entities
60
         * We try to get it from associationMapping (will throw a MappingException if not available)
61
         */
62
        $targetEntity = $meta->getReflectionProperty($pk)->getValue($entity);
63
64
        $mapping = $meta->getAssociationMapping($pk);
65
        $meta = $em->getClassMetadata($mapping['targetEntity']);
66
        $pk = $meta->getSingleIdentifierFieldName();
67
        $type = Type::getType($meta->fieldMappings[$pk]['type']);
68
69
        return $this->value($em, $type, $meta->getReflectionProperty($pk)->getValue($targetEntity));
70
    }
71
72
    /**
73
     * Computes a usable diff.
74
     *
75
     * @param EntityManager $em
76
     * @param object        $entity
77
     * @param array         $ch
78
     *
79
     * @throws \Doctrine\DBAL\DBALException
80
     * @throws \Doctrine\ORM\Mapping\MappingException
81
     *
82
     * @return array
83
     */
84
    public function diff(EntityManager $em, $entity, array $ch): array
85
    {
86
        $meta = $em->getClassMetadata(\get_class($entity));
87
        $diff = [];
88
89
        foreach ($ch as $fieldName => list($old, $new)) {
90
            $o = null;
91
            $n = null;
92
93
            if (
94
                $meta->hasField($fieldName) &&
95
                !isset($meta->embeddedClasses[$fieldName]) &&
96
                $this->configuration->isAuditedField($entity, $fieldName)
97
            ) {
98
                $mapping = $meta->fieldMappings[$fieldName];
99
                $type = Type::getType($mapping['type']);
100
                $o = $this->value($em, $type, $old);
101
                $n = $this->value($em, $type, $new);
102
            } elseif (
103
                $meta->hasAssociation($fieldName) &&
104
                $meta->isSingleValuedAssociation($fieldName) &&
105
                $this->configuration->isAuditedField($entity, $fieldName)
106
            ) {
107
                $o = $this->summarize($em, $old);
108
                $n = $this->summarize($em, $new);
109
            }
110
111
            if ($o !== $n) {
112
                $diff[$fieldName] = [
113
                    'old' => $o,
114
                    'new' => $n,
115
                ];
116
            }
117
        }
118
119
        return $diff;
120
    }
121
122
    /**
123
     * Type converts the input value and returns it.
124
     *
125
     * @param EntityManager $em
126
     * @param Type          $type
127
     * @param mixed         $value
128
     *
129
     * @throws \Doctrine\DBAL\DBALException
130
     *
131
     * @return mixed
132
     */
133
    private function value(EntityManager $em, Type $type, $value)
134
    {
135
        if (null === $value) {
136
            return null;
137
        }
138
139
        $platform = $em->getConnection()->getDatabasePlatform();
140
141
        switch ($type->getName()) {
142
            case Type::DECIMAL:
143
            case Type::BIGINT:
144
                $convertedValue = (string) $value;
145
146
                break;
147
            case Type::INTEGER:
148
            case Type::SMALLINT:
149
                $convertedValue = (int) $value;
150
151
                break;
152
            case Type::FLOAT:
153
            case Type::BOOLEAN:
154
                $convertedValue = $type->convertToPHPValue($value, $platform);
155
156
                break;
157
            default:
158
                $convertedValue = $type->convertToDatabaseValue($value, $platform);
159
        }
160
161
        return $convertedValue;
162
    }
163
164
    /**
165
     * Blames an audit operation.
166
     *
167
     * @return array
168
     */
169
    public function blame(): array
170
    {
171
        $user_id = null;
172
        $username = null;
173
        $client_ip = null;
174
        $user_fqdn = null;
175
        $user_firewall = null;
176
177
        $request = $this->configuration->getRequestStack()->getCurrentRequest();
178
        if (null !== $request) {
179
            $client_ip = $request->getClientIp();
180
            $user_firewall = null === $this->configuration->getFirewallMap()->getFirewallConfig($request) ? null : $this->configuration->getFirewallMap()->getFirewallConfig($request)->getName();
181
        }
182
183
        $user = $this->configuration->getUserProvider()->getUser();
184
        if ($user instanceof UserInterface) {
185
            $user_id = $user->getId();
186
            $username = $user->getUsername();
187
            $user_fqdn = \get_class($user);
188
        }
189
190
        return [
191
            'user_id' => $user_id,
192
            'username' => $username,
193
            'client_ip' => $client_ip,
194
            'user_fqdn' => $user_fqdn,
195
            'user_firewall' => $user_firewall,
196
        ];
197
    }
198
199
    /**
200
     * Returns an array describing an entity.
201
     *
202
     * @param EntityManager $em
203
     * @param object        $entity
204
     * @param mixed         $id
205
     *
206
     * @throws \Doctrine\DBAL\DBALException
207
     * @throws \Doctrine\ORM\Mapping\MappingException
208
     *
209
     * @return array
210
     */
211
    public function summarize(EntityManager $em, $entity = null, $id = null): ?array
212
    {
213
        if (null === $entity) {
214
            return null;
215
        }
216
217
        $em->getUnitOfWork()->initializeObject($entity); // ensure that proxies are initialized
218
        $meta = $em->getClassMetadata(\get_class($entity));
219
        $pkName = $meta->getSingleIdentifierFieldName();
220
        $pkValue = $id ?? $this->id($em, $entity);
221
        // An added guard for proxies that fail to initialize.
222
        if (null === $pkValue) {
223
            return null;
224
        }
225
226
        if (method_exists($entity, '__toString')) {
227
            $label = (string) $entity;
228
        } else {
229
            $label = \get_class($entity).'#'.$pkValue;
230
        }
231
232
        return [
233
            'label' => $label,
234
            'class' => $meta->name,
235
            'table' => $meta->getTableName(),
236
            $pkName => $pkValue,
237
        ];
238
    }
239
240
    /**
241
     * Return columns of audit tables.
242
     *
243
     * @return array
244
     */
245
    public function getAuditTableColumns(): array
246
    {
247
        return [
248
            'id' => [
249
                'type' => Type::INTEGER,
250
                'options' => [
251
                    'autoincrement' => true,
252
                    'unsigned' => true,
253
                ],
254
            ],
255
            'type' => [
256
                'type' => Type::STRING,
257
                'options' => [
258
                    'notnull' => true,
259
                    'length' => 10,
260
                ],
261
            ],
262
            'object_id' => [
263
                'type' => Type::STRING,
264
                'options' => [
265
                    'notnull' => true,
266
                ],
267
            ],
268
            'transaction_hash' => [
269
                'type' => Type::STRING,
270
                'options' => [
271
                    'notnull' => false,
272
                    'length' => 40,
273
                ],
274
            ],
275
            'diffs' => [
276
                'type' => Type::JSON_ARRAY,
277
                'options' => [
278
                    'default' => null,
279
                    'notnull' => false,
280
                ],
281
            ],
282
            'blame_id' => [
283
                'type' => Type::STRING,
284
                'options' => [
285
                    'default' => null,
286
                    'notnull' => false,
287
                    'unsigned' => true,
288
                ],
289
            ],
290
            'blame_user' => [
291
                'type' => Type::STRING,
292
                'options' => [
293
                    'default' => null,
294
                    'notnull' => false,
295
                    'length' => 255,
296
                ],
297
            ],
298
            'blame_user_fqdn' => [
299
                'type' => Type::STRING,
300
                'options' => [
301
                    'default' => null,
302
                    'notnull' => false,
303
                    'length' => 255,
304
                ],
305
            ],
306
            'blame_user_firewall' => [
307
                'type' => Type::STRING,
308
                'options' => [
309
                    'default' => null,
310
                    'notnull' => false,
311
                    'length' => 100,
312
                ],
313
            ],
314
            'ip' => [
315
                'type' => Type::STRING,
316
                'options' => [
317
                    'default' => null,
318
                    'notnull' => false,
319
                    'length' => 45,
320
                ],
321
            ],
322
            'created_at' => [
323
                'type' => Type::DATETIME,
324
                'options' => [
325
                    'notnull' => true,
326
                ],
327
            ],
328
        ];
329
    }
330
331
    public function getAuditTableIndices(string $tablename): array
332
    {
333
        return [
334
            'id' => [
335
                'type' => 'primary',
336
            ],
337
            'type' => [
338
                'type' => 'index',
339
                'name' => 'type_'.md5($tablename).'_idx',
340
            ],
341
            'object_id' => [
342
                'type' => 'index',
343
                'name' => 'object_id_'.md5($tablename).'_idx',
344
            ],
345
            'transaction_hash' => [
346
                'type' => 'index',
347
                'name' => 'transaction_hash_'.md5($tablename).'_idx',
348
            ],
349
            'blame_id' => [
350
                'type' => 'index',
351
                'name' => 'blame_id_'.md5($tablename).'_idx',
352
            ],
353
            'created_at' => [
354
                'type' => 'index',
355
                'name' => 'created_at_'.md5($tablename).'_idx',
356
            ],
357
        ];
358
    }
359
360
    public static function paramToNamespace(string $entity): string
361
    {
362
        return str_replace('-', '\\', $entity);
363
    }
364
365
    public static function namespaceToParam(string $entity): string
366
    {
367
        return str_replace('\\', '-', $entity);
368
    }
369
}
370