Completed
Pull Request — master (#88)
by Julien
02:57
created

UnitOfWork   A

Complexity

Total Complexity 37

Size/Duplication

Total Lines 246
Duplicated Lines 0 %

Test Coverage

Coverage 97.78%

Importance

Changes 0
Metric Value
eloc 98
dl 0
loc 246
ccs 88
cts 90
cp 0.9778
rs 9.44
c 0
b 0
f 0
wmc 37

9 Methods

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