Completed
Pull Request — master (#56)
by
unknown
02:11
created

Serializer::resolveRealClassName()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 14
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 14
rs 9.4285
cc 3
eloc 6
nc 3
nop 2
1
<?php
2
3
namespace Mapado\RestClientSdk\Model;
4
5
use Mapado\RestClientSdk\Exception\SdkException;
6
use Mapado\RestClientSdk\Helper\ArrayHelper;
7
use Mapado\RestClientSdk\Mapping;
8
use Mapado\RestClientSdk\Mapping\ClassMetadata;
9
use Mapado\RestClientSdk\SdkClient;
10
use libphonenumber\PhoneNumberFormat;
11
use libphonenumber\PhoneNumberUtil;
12
13
/**
14
 * Class Serializer
15
 * @author Julien Deniau <[email protected]>
16
 */
17
class Serializer
18
{
19
    /**
20
     * mapping
21
     *
22
     * @var Mapping
23
     * @access private
24
     */
25
    private $mapping;
26
27
    /**
28
     * @var SdkClient|null
29
     */
30
    private $sdk;
31
32
    /**
33
     * Constructor.
34
     *
35
     * @param Mapping $mapping
36
     * @access public
37
     */
38
    public function __construct(Mapping $mapping)
39
    {
40
        $this->mapping = $mapping;
41
    }
42
43
    /**
44
     * setSdk
45
     *
46
     * @param SdkClient $sdk
47
     * @access public
48
     * @return Serializer
49
     */
50
    public function setSdk(SdkClient $sdk)
51
    {
52
        $this->sdk = $sdk;
53
        return $this;
54
    }
55
56
    /**
57
     * serialize entity for POST and PUT
58
     *
59
     * @param object $entity
60
     * @param string $modelName
61
     * @param array  $context
62
     * @access public
63
     * @return array
64
     */
65
    public function serialize($entity, $modelName, $context = [])
66
    {
67
        return $this->recursiveSerialize($entity, $modelName, 0, $context);
68
    }
69
70
    /**
71
     * deserialize
72
     *
73
     * @param array  $data
74
     * @param string $className
75
     * @access public
76
     * @return object
77
     */
78
    public function deserialize(array $data, $className)
79
    {
80
        $className = $this->resolveRealClassName($data, $className);
81
82
        $classMetadata = $this->mapping->getClassMetadata($className);
83
        $identifierAttribute = $classMetadata->getIdentifierAttribute();
84
        $identifierAttrKey = $identifierAttribute ? $identifierAttribute->getSerializedKey() : null;
85
86
        $attributeList = $classMetadata->getAttributeList();
87
88
        $instance = new $className();
89
90
        foreach ($attributeList as $attribute) {
91
            $key = $attribute->getSerializedKey();
92
93
            if (!ArrayHelper::arrayHas($data, $key)) {
94
                continue;
95
            }
96
97
            $value = ArrayHelper::arrayGet($data, $key);
98
99
100
            $setter = 'set' . ucfirst($attribute->getAttributeName());
101
102
            if (method_exists($instance, $setter)) {
103
                $relation = $classMetadata->getRelation($key);
104
                if ($relation) {
105
                    if (is_string($value)) {
106
                        $value = $this->sdk->createProxy($value);
107
                    } elseif (is_array($value)) {
108
                        if (isset($value[$identifierAttrKey])) {
109
                            $key = $this->mapping->getKeyFromId($value[$identifierAttrKey]);
0 ignored issues
show
Unused Code introduced by
$key is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
110
                            $subClassMetadata = $this->getClassMetadataFromId($value[$identifierAttrKey]);
111
                            $value = $this->deserialize($value, $subClassMetadata->getModelName());
112
                        } else {
113
                            $list = [];
114
                            foreach ($value as $item) {
115
                                if (is_string($item)) {
116
                                    $list[] = $this->sdk->createProxy($item);
117
                                } elseif (is_array($item) && isset($item[$identifierAttrKey])) {
118
                                    // not tested for now
119
                                    // /the $identifierAttrKey is not the real identifier, as it is
120
                                    // the main object identifier, but we do not have the metadada for now
121
                                    // the thing we assume now is that every entity "may" have the same key
122
                                    // as identifier
123
                                    $key = $this->mapping->getKeyFromId($item[$identifierAttrKey]);
0 ignored issues
show
Unused Code introduced by
$key is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
124
                                    $subClassMetadata = $this->getClassMetadataFromId($item[$identifierAttrKey]);
125
                                    $list[] = $this->deserialize($item, $subClassMetadata->getModelName());
126
                                }
127
                            }
128
129
                            $value = $list;
130
                        }
131
                    }
132
                }
133
134
                if (isset($value)) {
135
                    if ($attribute && $attribute->getType() === 'datetime') {
136
                        $value = new \DateTime($value);
137
                    }
138
139
                    $instance->$setter($value);
140
                }
141
            }
142
        }
143
144
        return $instance;
145
    }
146
147
    /**
148
     * If provided class name is abstract (a base class), the real class name (child class)
149
     * may be available in some data fields.
150
     *
151
     * @param array  $data
152
     * @param string $className
153
     * @access private
154
     * @return string
155
     */
156
    private function resolveRealClassName(array $data, $className)
157
    {
158
        if (!empty($data['@type'])) {
159
            $classMetadata = $this->mapping->tryGetClassMetadataByShortName($data['@type']);
160
161
            if ($classMetadata) {
162
                return $classMetadata->getModelName();
163
            }
164
        }
165
166
        // @todo Try to resolve model key from IRI if available.
167
168
        return $className;
169
    }
170
171
    /**
172
     * recursiveSerialize
173
     *
174
     * @param object $entity
175
     * @param string $modelName
176
     * @param int    $level
177
     * @param array  $context
178
     * @access private
179
     * @return array|mixed
180
     */
181
    private function recursiveSerialize($entity, $modelName, $level = 0, $context = [])
182
    {
183
        $classMetadata = $this->mapping->getClassMetadata($modelName);
184
185
        if ($level > 0 && empty($context['serializeRelation'])) {
186
            $idAttribute = $classMetadata->getIdentifierAttribute();
187
            $getter = 'get' . ucfirst($idAttribute->getAttributeName());
188
            $tmpId = $entity->{$getter}();
189
            if ($tmpId) {
190
                return $tmpId;
191
            }
192
        }
193
194
        $attributeList = $classMetadata->getAttributeList();
195
196
        $out = [];
197
        if (!empty($attributeList)) {
198
            foreach ($attributeList as $attribute) {
199
                $method = 'get' . ucfirst($attribute->getAttributeName());
200
201
                if ($attribute->isIdentifier() && !$entity->$method()) {
202
                    continue;
203
                }
204
                $relation = $classMetadata->getRelation($attribute->getSerializedKey());
205
206
                $data = $entity->$method();
207
208
                if (null === $data && $relation && $relation->isManyToOne() && $level > 0) {
209
                    /*
210
                        We only serialize the root many-to-one relations to prevent, hopefully,
211
                        unlinked and/or duplicated content. For instance, a cart with cartItemList containing
212
                        null values for the cart [{ cart => null, ... }] may lead the creation of
213
                        CartItem entities explicitly bound to a null Cart instead of the created/updated Cart.
214
                     */
215
                    continue;
216
                } elseif ($data instanceof \DateTime) {
217
                    $data = $data->format('c');
218
                } elseif (is_object($data) && get_class($data) === "libphonenumber\PhoneNumber") {
219
                    $phoneNumberUtil = PhoneNumberUtil::getInstance();
220
                    $data = $phoneNumberUtil->format(
221
                        $data,
222
                        PhoneNumberFormat::INTERNATIONAL
223
                    );
224
                } elseif (is_object($data)
225
                    && $relation
226
                    && $this->mapping->hasClassMetadata($relation->getTargetEntity())
227
                ) {
228
                    $idAttribute = $this->mapping
229
                        ->getClassMetadata($relation->getTargetEntity())
230
                        ->getIdentifierAttribute()
231
                        ->getAttributeName();
232
                    $idGetter = 'get' . ucfirst($idAttribute);
233
234
                    if (method_exists($data, $idGetter) && $data->{$idGetter}()) {
235
                        $data = $data->{$idGetter}();
236
                    } elseif ($relation->isManyToOne()) {
237
                        if ($level > 0) {
238
                            continue;
239
                        } else {
240
                            throw new SdkException('Case not allowed for now');
241
                        }
242
                    }
243
                } elseif (is_array($data)) {
244
                    $newData = [];
245
                    foreach ($data as $key => $item) {
246
                        if ($item instanceof \DateTime) {
247
                            $newData[$key] = $item->format('c');
248
                        } elseif (is_object($item) &&
249
                            $relation &&
250
                            $this->mapping->hasClassMetadata($relation->getTargetEntity())
251
                        ) {
252
                            $serializeRelation = !empty($context['serializeRelations'])
253
                                && in_array($relation->getSerializedKey(), $context['serializeRelations']);
254
255
                            $newData[$key] = $this->recursiveSerialize(
256
                                $item,
257
                                $relation->getTargetEntity(),
258
                                $level + 1,
259
                                [ 'serializeRelation' => $serializeRelation ]
260
                            );
261
                        } else {
262
                            $newData[$key] = $item;
263
                        }
264
                    }
265
                    $data = $newData;
266
                }
267
268
                $key = $attribute->getSerializedKey();
269
270
                $out[$key] = $data;
271
            }
272
        }
273
274
        return $out;
275
    }
276
277
    /**
278
     * getClassMetadataFromId
279
     *
280
     * @param string $id
281
     * @access private
282
     * @return ClassMetadata|null
283
     */
284
    private function getClassMetadataFromId($id)
285
    {
286
        $key = $this->mapping->getKeyFromId($id);
287
        $classMetadata = $this->mapping->getClassMetadataByKey($key);
288
        return $classMetadata;
289
    }
290
}
291