Completed
Pull Request — master (#155)
by Alex
03:15
created

CynicDeserialiser::processLinkSingleton()   B

Complexity

Conditions 8
Paths 12

Size

Total Lines 55
Code Lines 43

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 55
rs 7.4033
cc 8
eloc 43
nc 12
nop 5

How to fix   Long Method   

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