UnitOfWork::getDirtyFields()   D
last analyzed

Complexity

Conditions 21
Paths 22

Size

Total Lines 112
Code Lines 65

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 54
CRAP Score 21

Importance

Changes 8
Bugs 1 Features 1
Metric Value
cc 21
eloc 65
c 8
b 1
f 1
nc 22
nop 3
dl 0
loc 112
ccs 54
cts 54
cp 1
crap 21
rs 4.1666

How to fix   Long Method    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
declare(strict_types=1);
4
5
namespace Mapado\RestClientSdk;
6
7
use Mapado\RestClientSdk\Helper\ArrayHelper;
8
use Mapado\RestClientSdk\Mapping\ClassMetadata;
9
use Mapado\RestClientSdk\Mapping\Relation;
10
11
/**
12
 * UnitOfWork
13
 */
14
class UnitOfWork
15
{
16
    /**
17
     * mapping
18
     *
19
     * @var Mapping
20
     */
21
    private $mapping;
22
23
    /**
24
     * storage for every entity retrieved
25
     *
26
     * @var array<string, object>
27
     */
28
    private $storage;
29
30
    /**
31
     * Constructor.
32
     */
33
    public function __construct(Mapping $mapping)
34
    {
35 1
        $this->mapping = $mapping;
36 1
        $this->storage = [];
37 1
    }
38
39
    /**
40
     * return the new serialized model with only needed fields to update
41
     */
42
    public function getDirtyData(
43
        array $newSerializedModel,
44
        array $oldSerializedModel,
45
        ClassMetadata $classMetadata
46
    ): array {
47 1
        return $this->getDirtyFields(
48 1
            $newSerializedModel,
49
            $oldSerializedModel,
50
            $classMetadata
51
        );
52
    }
53
54
    /**
55
     * Register a net entity in the UnitOfWork storage
56
     */
57
    public function registerClean(string $id, object $entity): self
58
    {
59 1
        $entityStored = clone $entity;
60 1
        $this->storage[$id] = $entityStored;
61
62 1
        return $this;
63
    }
64
65
    public function getDirtyEntity(string $id): ?object
66
    {
67 1
        return $this->storage[$id] ?? null;
68
    }
69
70
    public function clear(string $id): self
71
    {
72
        unset($this->storage[$id]);
73
74
        return $this;
75
    }
76
77
    /**
78
     * Compare serialize object and returns only modified fields
79
     */
80
    private function getDirtyFields(
81
        array $newSerializedModel,
82
        array $oldSerializedModel,
83
        ClassMetadata $classMetadata
84
    ): array {
85 1
        $dirtyFields = [];
86
87 1
        foreach ($newSerializedModel as $key => $value) {
88 1
            if (!array_key_exists($key, $oldSerializedModel)) {
89
                // a new key has been found, add it to the dirtyFields
90 1
                $dirtyFields[$key] = $value;
91 1
                continue;
92
            }
93
94 1
            $oldValue = $oldSerializedModel[$key];
95
96 1
            $currentRelation = $classMetadata->getRelation($key);
97
98 1
            if (!$currentRelation) {
99
                if (
100 1
                    (is_array($value) &&
101 1
                        !ArrayHelper::arraySame($value, $oldValue ?: [])) ||
102 1
                    $value !== $oldValue
103
                ) {
104 1
                    $dirtyFields[$key] = $value;
105
                }
106 1
                continue;
107
            }
108
109 1
            $currentClassMetadata = $this->mapping->getClassMetadata(
110 1
                $currentRelation->getTargetEntity()
111
            );
112
113 1
            $idSerializedKey = $currentClassMetadata->getIdSerializeKey();
114
115 1
            if (Relation::MANY_TO_ONE === $currentRelation->getType()) {
116 1
                if ($value !== $oldValue) {
117 1
                    if (is_string($value) || is_string($oldValue)) {
118 1
                        $dirtyFields[$key] = $value;
119
                    } else {
120 1
                        $recursiveDiff = $this->getDirtyFields(
121 1
                            $value,
122 1
                            $oldValue,
123 1
                            $currentClassMetadata
124
                        );
125
126 1
                        if (!empty($recursiveDiff)) {
127
                            $recursiveDiff[
128 1
                                $idSerializedKey
129 1
                            ] = self::getEntityId($value, $idSerializedKey);
130 1
                            $dirtyFields[$key] = $recursiveDiff;
131
                        }
132
                    }
133
                }
134
135 1
                continue;
136
            }
137
138
            // ONE_TO_MANY relation
139 1
            if (count($value ?? []) !== count($oldValue ?? [])) {
140
                // get all objects ids of new array
141 1
                $dirtyFields[$key] = $this->addIdentifiers(
142 1
                    $value,
143 1
                    [],
144
                    $idSerializedKey
145
                );
146
            }
147
148 1
            if (!empty($value)) {
149 1
                foreach ($value as $relationKey => $relationValue) {
150 1
                    $oldRelationValue = $this->findOldRelation(
151 1
                        $relationValue,
152
                        $oldValue,
153
                        $currentClassMetadata
154
                    );
155
156 1
                    if ($relationValue !== $oldRelationValue) {
157
                        if (
158 1
                            is_string($relationValue) ||
159 1
                            is_string($oldRelationValue)
160
                        ) {
161 1
                            $dirtyFields[$key][$relationKey] = $relationValue;
162
                        } else {
163 1
                            $recursiveDiff = $this->getDirtyFields(
164 1
                                $relationValue,
165 1
                                $oldRelationValue,
166 1
                                $currentClassMetadata
167
                            );
168
169 1
                            if (!empty($recursiveDiff)) {
170 1
                                $idSerializedKey = $currentClassMetadata->getIdSerializeKey();
171
172 1
                                $entityId = self::getEntityId(
173 1
                                    $relationValue,
174
                                    $idSerializedKey
175
                                );
176 1
                                if (null !== $entityId) {
177
                                    $recursiveDiff[
178 1
                                        $idSerializedKey
179
                                    ] = $entityId;
180
                                }
181 1
                                $dirtyFields[$key][
182 1
                                    $relationKey
183
                                ] = $recursiveDiff;
184
                            }
185
                        }
186
                    }
187
                }
188
            }
189
        }
190
191 1
        return $dirtyFields;
192
    }
193
194
    /**
195
     * add defined identifiers to given model
196
     *
197
     * @param mixed $newSerializedModel
198
     * @param ?mixed $idSerializedKey
199
     */
200
    private function addIdentifiers(
201
        $newSerializedModel,
202
        array $dirtyFields,
203
        $idSerializedKey = null
204
    ): array {
205 1
        foreach ($newSerializedModel as $key => $value) {
206 1
            if ($idSerializedKey && isset($value[$idSerializedKey])) {
207 1
                $dirtyFields[$key][$idSerializedKey] = $value[$idSerializedKey];
208 1
            } elseif (is_string($value) && is_int($key)) {
209 1
                $dirtyFields[$key] = $value;
210
            }
211
        }
212
213 1
        return $dirtyFields;
214
    }
215
216
    /**
217
     * @param string|array $relationValue
218
     *
219
     * @return array|string
220
     */
221
    private function findOldRelation(
222
        $relationValue,
223
        array $oldValue,
224
        ClassMetadata $classMetadata
225
    ) {
226 1
        $idSerializedKey = $classMetadata->getIdSerializeKey();
227
228 1
        $relationValueId = self::getEntityId($relationValue, $idSerializedKey);
229
230 1
        foreach ($oldValue as $oldRelationValue) {
231 1
            $oldRelationValueId = self::getEntityId(
232 1
                $oldRelationValue,
233
                $idSerializedKey
234
            );
235
236 1
            if ($relationValueId === $oldRelationValueId) {
237 1
                return $oldRelationValue;
238
            }
239
        }
240
241 1
        return $classMetadata->getDefaultSerializedModel();
242
    }
243
244
    /**
245
     * get entity id from string or array
246
     *
247
     * @param string|array $stringOrEntity
248
     *
249
     * @return ?mixed
250
     */
251
    private static function getEntityId(
252
        $stringOrEntity,
253
        string $idSerializedKey
254
    ) {
255 1
        if (!is_array($stringOrEntity)) {
256 1
            return $stringOrEntity;
257
        }
258
259 1
        return $stringOrEntity[$idSerializedKey] ?? null;
260
    }
261
}
262