Passed
Push — master ( 01c95e...1b44dd )
by Julien
01:08
created

UnitOfWork::getDirtyEntity()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 1
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 1
dl 0
loc 3
ccs 1
cts 1
cp 1
crap 1
rs 10
c 0
b 0
f 0
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 1
            $oldSerializedModel,
50 1
            $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) &&
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...
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 1
                    $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 1
                        $oldValue,
153 1
                        $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 1
                                    $idSerializedKey
175
                                );
176 1
                                if (null !== $entityId) {
177
                                    $recursiveDiff[
178 1
                                        $idSerializedKey
179
                                    ] = $entityId;
180
                                }
181 1
                                $dirtyFields[$key][
182 1
                                    $relationKey
183 1
                                ] = $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 $idSerializedKey
198
     */
199
    private function addIdentifiers(
200
        $newSerializedModel,
201
        array $dirtyFields,
202
        $idSerializedKey = null
203
    ): array {
204 1
        foreach ($newSerializedModel as $key => $value) {
205 1
            if ($idSerializedKey && isset($value[$idSerializedKey])) {
206 1
                $dirtyFields[$key][$idSerializedKey] = $value[$idSerializedKey];
207 1
            } elseif (is_string($value) && is_int($key)) {
208 1
                $dirtyFields[$key] = $value;
209
            }
210
        }
211
212 1
        return $dirtyFields;
213
    }
214
215
    /**
216
     * @param string|array $relationValue
217
     *
218
     * @return array|string
219
     */
220
    private function findOldRelation(
221
        $relationValue,
222
        array $oldValue,
223
        ClassMetadata $classMetadata
224
    ) {
225 1
        $idSerializedKey = $classMetadata->getIdSerializeKey();
226
227 1
        $relationValueId = self::getEntityId($relationValue, $idSerializedKey);
228
229 1
        foreach ($oldValue as $oldRelationValue) {
230 1
            $oldRelationValueId = self::getEntityId(
231 1
                $oldRelationValue,
232 1
                $idSerializedKey
233
            );
234
235 1
            if ($relationValueId === $oldRelationValueId) {
236 1
                return $oldRelationValue;
237
            }
238
        }
239
240 1
        return $classMetadata->getDefaultSerializedModel();
241
    }
242
243
    /**
244
     * get entity id from string or array
245
     *
246
     * @param string|array $stringOrEntity
247
     * @param string $idSerializedKey
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