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

AuditHelper::diff()   B

Complexity

Conditions 10
Paths 6

Size

Total Lines 34
Code Lines 23

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 10
eloc 23
nc 6
nop 3
dl 0
loc 34
rs 7.6666
c 0
b 0
f 0

How to fix   Complexity   

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
            // Primary key is not part of fieldMapping
51
            // @see https://github.com/DamienHarper/DoctrineAuditBundle/issues/40
52
            // @see https://www.doctrine-project.org/projects/doctrine-orm/en/latest/tutorials/composite-primary-keys.html#identity-through-foreign-entities
53
            // We try to get it from associationMapping (will throw a MappingException if not available)
54
            $targetEntity = $meta->getReflectionProperty($pk)->getValue($entity);
55
56
            $mapping = $meta->getAssociationMapping($pk);
57
            $meta = $em->getClassMetadata($mapping['targetEntity']);
58
            $pk = $meta->getSingleIdentifierFieldName();
59
            $type = Type::getType($meta->fieldMappings[$pk]['type']);
60
61
            return $this->value($em, $type, $meta->getReflectionProperty($pk)->getValue($targetEntity));
62
        }
63
64
        $type = Type::getType($meta->fieldMappings[$pk]['type']);
65
66
        return $this->value($em, $type, $meta->getReflectionProperty($pk)->getValue($entity));
67
    }
68
69
    /**
70
     * Computes a usable diff.
71
     *
72
     * @param EntityManager $em
73
     * @param object        $entity
74
     * @param array         $ch
75
     *
76
     * @throws \Doctrine\DBAL\DBALException
77
     * @throws \Doctrine\ORM\Mapping\MappingException
78
     *
79
     * @return array
80
     */
81
    public function diff(EntityManager $em, $entity, array $ch): array
82
    {
83
        $meta = $em->getClassMetadata(\get_class($entity));
84
        $diff = [];
85
        foreach ($ch as $fieldName => list($old, $new)) {
86
            if ($meta->hasField($fieldName) && !isset($meta->embeddedClasses[$fieldName]) &&
87
                $this->configuration->isAuditedField($entity, $fieldName)
88
            ) {
89
                $mapping = $meta->fieldMappings[$fieldName];
90
                $type = Type::getType($mapping['type']);
91
                $o = $this->value($em, $type, $old);
92
                $n = $this->value($em, $type, $new);
93
                if ($o !== $n) {
94
                    $diff[$fieldName] = [
95
                        'old' => $o,
96
                        'new' => $n,
97
                    ];
98
                }
99
            } elseif ($meta->hasAssociation($fieldName) &&
100
                $meta->isSingleValuedAssociation($fieldName) &&
101
                $this->configuration->isAuditedField($entity, $fieldName)
102
            ) {
103
                $o = $this->summarize($em, $old);
104
                $n = $this->summarize($em, $new);
105
                if ($o !== $n) {
106
                    $diff[$fieldName] = [
107
                        'old' => $o,
108
                        'new' => $n,
109
                    ];
110
                }
111
            }
112
        }
113
114
        return $diff;
115
    }
116
117
    /**
118
     * Type converts the input value and returns it.
119
     *
120
     * @param EntityManager $em
121
     * @param Type          $type
122
     * @param mixed         $value
123
     *
124
     * @throws \Doctrine\DBAL\DBALException
125
     *
126
     * @return mixed
127
     */
128
    private function value(EntityManager $em, Type $type, $value)
129
    {
130
        if (null === $value) {
131
            return null;
132
        }
133
134
        $platform = $em->getConnection()->getDatabasePlatform();
135
136
        switch ($type->getName()) {
137
            case Type::DECIMAL:
138
            case Type::BIGINT:
139
                $convertedValue = (string) $value;
140
141
                break;
142
            case Type::INTEGER:
143
            case Type::SMALLINT:
144
                $convertedValue = (int) $value;
145
146
                break;
147
            case Type::FLOAT:
148
            case Type::BOOLEAN:
149
                $convertedValue = $type->convertToPHPValue($value, $platform);
150
151
                break;
152
            default:
153
                $convertedValue = $type->convertToDatabaseValue($value, $platform);
154
        }
155
156
        return $convertedValue;
157
    }
158
159
    /**
160
     * Blames an audit operation.
161
     *
162
     * @return array
163
     */
164
    public function blame(): array
165
    {
166
        $user_id = null;
167
        $username = null;
168
        $client_ip = null;
169
170
        $request = $this->configuration->getRequestStack()->getCurrentRequest();
171
        if (null !== $request) {
172
            $client_ip = $request->getClientIp();
173
        }
174
175
        $user = $this->configuration->getUserProvider()->getUser();
176
        if ($user instanceof UserInterface) {
177
            $user_id = $user->getId();
178
            $username = $user->getUsername();
179
        }
180
181
        return [
182
            'user_id' => $user_id,
183
            'username' => $username,
184
            'client_ip' => $client_ip,
185
        ];
186
    }
187
188
    /**
189
     * Returns an array describing an entity.
190
     *
191
     * @param EntityManager $em
192
     * @param object        $entity
193
     * @param mixed         $id
194
     *
195
     * @throws \Doctrine\DBAL\DBALException
196
     * @throws \Doctrine\ORM\Mapping\MappingException
197
     *
198
     * @return array
199
     */
200
    public function summarize(EntityManager $em, $entity = null, $id = null): ?array
201
    {
202
        if (null === $entity) {
203
            return null;
204
        }
205
206
        $em->getUnitOfWork()->initializeObject($entity); // ensure that proxies are initialized
207
        $meta = $em->getClassMetadata(\get_class($entity));
208
        $pkName = $meta->getSingleIdentifierFieldName();
209
        $pkValue = $id ?? $this->id($em, $entity);
210
        if (method_exists($entity, '__toString')) {
211
            $label = (string) $entity;
212
        } else {
213
            $label = \get_class($entity).'#'.$pkValue;
214
        }
215
216
        return [
217
            'label' => $label,
218
            'class' => $meta->name,
219
            'table' => $meta->table['name'],
220
            $pkName => $pkValue,
221
        ];
222
    }
223
}
224