Completed
Push — master ( 561959...44ef42 )
by Alex
02:58
created

CynicDeserialiser::isEntryOK()   C

Complexity

Conditions 11
Paths 41

Size

Total Lines 39
Code Lines 25

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 39
rs 5.2653
cc 11
eloc 25
nc 41
nop 1

How to fix   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
namespace POData\ObjectModel;
4
5
use POData\Providers\Metadata\IMetadataProvider;
6
use POData\Providers\Metadata\ResourceEntityType;
7
use POData\Providers\Metadata\ResourceSet;
8
use POData\Providers\Metadata\Type\IType;
9
use POData\Providers\ProvidersWrapper;
10
use POData\Providers\Query\IQueryProvider;
11
use POData\UriProcessor\ResourcePathProcessor\SegmentParser\KeyDescriptor;
12
13
class CynicDeserialiser
14
{
15
    /**
16
     * @var IMetadataProvider
17
     */
18
    private $metaProvider;
19
20
    /**
21
     * @var ProvidersWrapper
22
     */
23
    private $wrapper;
24
25
    /**
26
     * @var ModelDeserialiser
27
     */
28
    private $cereal;
29
30
    public function __construct(IMetadataProvider $meta, ProvidersWrapper $wrapper)
31
    {
32
        $this->metaProvider = $meta;
33
        $this->wrapper = $wrapper;
34
        $this->cereal = new ModelDeserialiser();
35
    }
36
37
    /**
38
     * @param ODataEntry $payload
39
     */
40
    public function processPayload(ODataEntry &$payload)
41
    {
42
        assert($this->isEntryOK($payload));
43
        list($sourceSet, $source) = $this->processEntryContent($payload);
44
        assert($sourceSet instanceof ResourceSet);
45
        $numLinks = count($payload->links);
46
        for ($i = 0; $i < $numLinks; $i++) {
47
            $this->processLink($payload->links[$i], $sourceSet, $source);
48
        }
49
        return $source;
50
    }
51
52
    protected function isEntryOK(ODataEntry $payload)
53
    {
54
        // check links
55
        foreach ($payload->links as $link) {
56
            $hasUrl = isset($link->url);
57
            $hasExpanded = isset($link->expandedResult);
58
            if ($hasUrl) {
59
                if (!is_string($link->url)) {
60
                    $msg = 'Url must be either string or null';
61
                    throw new \InvalidArgumentException($msg);
62
                }
63
            }
64
            if ($hasExpanded) {
65
                $isGood = $link->expandedResult instanceof ODataEntry || $link->expandedResult instanceof ODataFeed;
66
                if (!$isGood) {
67
                    $msg = 'Expanded result must null, or be instance of ODataEntry or ODataFeed';
68
                    throw new \InvalidArgumentException($msg);
69
                }
70
            }
71
            $isEntry = $link->expandedResult instanceof ODataEntry;
72
73
            if ($hasExpanded) {
74
                if ($isEntry) {
75
                    $this->isEntryOK($link->expandedResult);
76
                } else {
77
                    foreach ($link->expandedResult->entries as $expanded) {
78
                        $this->isEntryOK($expanded);
79
                    }
80
                }
81
            }
82
        }
83
84
        $set = $this->getMetaProvider()->resolveResourceSet($payload->resourceSetName);
85
        if (null === $set) {
86
            $msg = 'Specified resource set could not be resolved';
87
            throw new \InvalidArgumentException($msg);
88
        }
89
        return true;
90
    }
91
92
    protected function processEntryContent(ODataEntry &$content)
93
    {
94
        assert(null === $content->id || is_string($content->id), 'Entry id must be null or string');
95
96
        $isCreate = null === $content->id || empty($content->id);
97
        $set = $this->getMetaProvider()->resolveResourceSet($content->resourceSetName);
98
        assert($set instanceof ResourceSet, get_class($set));
99
        $type = $set->getResourceType();
100
        $properties = $this->getDeserialiser()->bulkDeserialise($type, $content);
101
        $properties = (object) $properties;
102
103
        if ($isCreate) {
104
            $result = $this->getWrapper()->createResourceforResourceSet($set, null, $properties);
105
            assert(isset($result), get_class($result));
106
            $key = $this->generateKeyDescriptor($type, $result);
107
        } else {
108
            $key = $this->generateKeyDescriptor($type, $content->propertyContent, $content->id);
109
            assert($key instanceof KeyDescriptor, get_class($key));
110
            $source = $this->getWrapper()->getResourceFromResourceSet($set, $key);
111
            assert(isset($source), get_class($source));
112
            $result = $this->getWrapper()->updateResource($set, $source, $key, $properties);
113
        }
114
115
        $content->id = $key;
116
        return [$set, $result];
117
    }
118
119
    /**
120
     * @return IMetadataProvider
121
     */
122
    protected function getMetaProvider()
123
    {
124
        return $this->metaProvider;
125
    }
126
127
    /**
128
     * @return ProvidersWrapper
129
     */
130
    protected function getWrapper()
131
    {
132
        return $this->wrapper;
133
    }
134
135
    /**
136
     * @return ModelDeserialiser
137
     */
138
    protected function getDeserialiser()
139
    {
140
        return $this->cereal;
141
    }
142
143
    /**
144
     * @param ResourceEntityType $type
145
     * @param ODataPropertyContent|object $result
146
     * @param string|null $id
147
     * @return null|KeyDescriptor
148
     */
149
    protected function generateKeyDescriptor(ResourceEntityType $type, $result, $id = null)
150
    {
151
        $isOData = $result instanceof ODataPropertyContent;
152
        $keyProp = $type->getKeyProperties();
153
        if (null === $id) {
154
            $keyPredicate = '';
155
            foreach ($keyProp as $prop) {
156
                $iType = $prop->getInstanceType();
157
                assert($iType instanceof IType, get_class($iType));
158
                $keyName = $prop->getName();
159
                $rawKey = $isOData ? $result->properties[$keyName]->value : $result->$keyName;
160
                $keyVal = $iType->convertToOData($rawKey);
161
                assert(isset($keyVal), 'Key property ' . $keyName . ' must not be null');
162
                $keyPredicate .= $keyName . '=' . $keyVal . ', ';
163
            }
164
            $keyPredicate[strlen($keyPredicate) - 2] = ' ';
165
        } else {
166
            $idBits = explode('/', $id);
167
            $keyRaw = $idBits[count($idBits)-1];
168
            $rawBits = explode('(', $keyRaw, 2);
169
            $rawBits = explode(')', $rawBits[count($rawBits)-1]);
170
            $keyPredicate = $rawBits[0];
171
        }
172
        $keyPredicate = trim($keyPredicate);
173
        $keyDesc = null;
174
        $isParsed = KeyDescriptor::tryParseKeysFromKeyPredicate($keyPredicate, $keyDesc);
175
        assert(true === $isParsed, 'Key descriptor not successfully parsed');
176
        $keyDesc->validate($keyPredicate, $type);
177
        // this is deliberate - ODataEntry/Feed has the structure we need for processing, and we're inserting
178
        // keyDescriptor objects in id fields to indicate the given record has been processed
179
        return $keyDesc;
180
    }
181
182
    protected function processLink(ODataLink &$link, ResourceSet $sourceSet, $source)
183
    {
184
        $hasUrl = isset($link->url);
185
        $hasPayload = isset($link->expandedResult);
186
        assert(
187
            null == $link->expandedResult
188
            || $link->expandedResult instanceof ODataEntry
189
            || $link->expandedResult instanceof ODataFeed,
190
            get_class($link->expandedResult)
191
        );
192
        $isFeed = $link->expandedResult instanceof ODataFeed;
193
194
        // if nothing to hook up, bail out now
195
        if (!$hasUrl && !$hasPayload) {
196
            return;
197
        }
198
199
        if ($isFeed) {
200
            $this->processLinkFeed($link, $sourceSet, $source, $hasUrl, $hasPayload);
201
        } else {
202
            $this->processLinkSingleton($link, $sourceSet, $source, $hasUrl, $hasPayload);
203
        }
204
        return;
205
    }
206
207
    /**
208
     * @param ODataLink $link
209
     * @param ResourceSet $sourceSet
210
     * @param $source
211
     * @param $hasUrl
212
     * @param $hasPayload
213
     */
214
    protected function processLinkSingleton(ODataLink &$link, ResourceSet $sourceSet, $source, $hasUrl, $hasPayload)
215
    {
216
        assert(
217
            null === $link->expandedResult || $link->expandedResult instanceof ODataEntry,
218
            get_class($link->expandedResult)
219
        );
220
        if ($hasUrl) {
221
            $urlBitz = explode('/', $link->url);
222
            $rawPredicate = $urlBitz[count($urlBitz) - 1];
223
            $rawPredicate = explode('(', $rawPredicate);
224
            $setName = $rawPredicate[0];
225
            $rawPredicate = trim($rawPredicate[count($rawPredicate) - 1], ')');
226
            $targSet = $this->getMetaProvider()->resolveResourceSet($setName);
227
            assert(null !== $targSet, get_class($targSet));
228
            $type = $targSet->getResourceType();
229
        } else {
230
            $type = $this->getMetaProvider()->resolveResourceType($link->expandedResult->type->term);
231
        }
232
        assert($type instanceof ResourceEntityType, get_class($type));
233
        $propName = $link->title;
234
235
        if ($hasUrl) {
236
            $keyDesc = null;
237
            assert(isset($rawPredicate));
238
            KeyDescriptor::tryParseKeysFromKeyPredicate($rawPredicate, $keyDesc);
239
            $keyDesc->validate($rawPredicate, $type);
240
            assert(null !== $keyDesc, 'Key description must not be null');
241
        }
242
243
        // hooking up to existing resource
244
        if ($hasUrl && !$hasPayload) {
245
            assert(isset($targSet));
246
            assert(isset($keyDesc));
247
            $target = $this->getWrapper()->getResourceFromResourceSet($targSet, $keyDesc);
248
            assert(isset($target));
249
            $this->getWrapper()->hookSingleModel($sourceSet, $source, $targSet, $target, $propName);
250
            $link->url = $keyDesc;
251
            return;
252
        }
253
        // creating new resource
254
        if (!$hasUrl && $hasPayload) {
255
            list($targSet, $target) = $this->processEntryContent($link->expandedResult);
256
            assert(isset($target));
257
            $key = $this->generateKeyDescriptor($type, $link->expandedResult->propertyContent);
258
            $link->url = $key;
259
            $link->expandedResult->id = $key;
260
            $this->getWrapper()->hookSingleModel($sourceSet, $source, $targSet, $target, $propName);
261
            return;
262
        }
263
        // updating existing resource and connecting to it
264
        list($targSet, $target) = $this->processEntryContent($link->expandedResult);
265
        assert(isset($target));
266
        $link->url = $keyDesc;
267
        $link->expandedResult->id = $keyDesc;
268
        $this->getWrapper()->hookSingleModel($sourceSet, $source, $targSet, $target, $propName);
269
        return;
270
    }
271
272
    protected function processLinkFeed(ODataLink &$link, ResourceSet $sourceSet, $source, $hasUrl, $hasPayload)
273
    {
274
        assert(
275
            $link->expandedResult instanceof ODataFeed,
276
            get_class($link->expandedResult)
277
        );
278
        $propName = $link->title;
279
280
        // if entries is empty, bail out - nothing to do
281
        $numEntries = count($link->expandedResult->entries);
282
        if (0 === $numEntries) {
283
            return;
284
        }
285
        // check that each entry is of consistent resource set
286
        $first = $link->expandedResult->entries[0]->resourceSetName;
287
        for ($i = 1; $i < $numEntries; $i++) {
288
            if ($first !== $link->expandedResult->entries[$i]->resourceSetName) {
289
                $msg = 'All entries in given feed must have same resource set';
290
                throw new \InvalidArgumentException($msg);
291
            }
292
        }
293
294
        $targSet = $this->getMetaProvider()->resolveResourceSet($first);
295
        assert($targSet instanceof ResourceSet);
296
        $targType = $targSet->getResourceType();
297
        assert($targType instanceof ResourceEntityType);
298
        $instanceType = $targType->getInstanceType();
299
        assert($instanceType instanceof \ReflectionClass);
300
        $targObj = $instanceType->newInstanceArgs();
301
302
        // assemble payload
303
        $data = [];
304
        $keys = [];
305
        for ($i = 0; $i < $numEntries; $i++) {
306
            $data[] = $this->getDeserialiser()->bulkDeserialise(
307
                $targType,
308
                $link->expandedResult->entries[$i]
309
            );
310
            $keys[] = $hasUrl ? $this->generateKeyDescriptor(
311
                $targType,
312
                $link->expandedResult->entries[$i]->propertyContent
313
            ) : null;
314
        }
315
316
        // creation
317
        if (!$hasUrl && $hasPayload) {
318
            $bulkResult = $this->getWrapper()->createBulkResourceforResourceSet($targSet, $data);
319
            assert(is_array($bulkResult));
320 View Code Duplication
            for ($i = 0; $i < $numEntries; $i++) {
321
                $targEntityInstance = $bulkResult[$i];
322
                $this->getWrapper()->hookSingleModel($sourceSet, $source, $targSet, $targEntityInstance, $propName);
323
                $key = $this->generateKeyDescriptor($targType, $targEntityInstance);
324
                $link->expandedResult->entries[$i]->id = $key;
325
            }
326
        }
327
        // update
328
        if ($hasUrl && $hasPayload) {
329
            $bulkResult = $this->getWrapper()->updateBulkResource($targSet, $targObj, $keys, $data);
330 View Code Duplication
            for ($i = 0; $i < $numEntries; $i++) {
331
                $targEntityInstance = $bulkResult[$i];
332
                $this->getWrapper()->hookSingleModel($sourceSet, $source, $targSet, $targEntityInstance, $propName);
333
                $link->expandedResult->entries[$i]->id = $keys[$i];
334
            }
335
        }
336
        assert(isset($bulkResult) && is_array($bulkResult));
337
338
        for ($i = 0; $i < $numEntries; $i++) {
339
            $numLinks = count($link->expandedResult->entries[$i]->links);
340
            for ($j = 0; $j < $numLinks; $j++) {
341
                $this->processLink($link->expandedResult->entries[$i]->links[$j], $targSet, $bulkResult[$i]);
342
            }
343
        }
344
345
        return;
346
    }
347
}
348