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

AuditHelper::id()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 23
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Importance

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