Passed
Pull Request — master (#206)
by Alex
05:00
created

IronicSerialiser::writeComplexValue()   B

Complexity

Conditions 9
Paths 14

Size

Total Lines 51
Code Lines 32

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
eloc 32
c 2
b 0
f 0
dl 0
loc 51
rs 8.0555
cc 9
nc 14
nop 3

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 AlgoWeb\PODataLaravel\Serialisers;
4
5
use Carbon\Carbon;
6
use Illuminate\Database\Eloquent\Model;
7
use Illuminate\Support\Collection;
8
use Illuminate\Support\Facades\App;
9
use POData\Common\InvalidOperationException;
10
use POData\Common\Messages;
11
use POData\Common\ODataConstants;
12
use POData\Common\ODataException;
13
use POData\IService;
14
use POData\ObjectModel\IObjectSerialiser;
15
use POData\ObjectModel\ODataBagContent;
16
use POData\ObjectModel\ODataCategory;
17
use POData\ObjectModel\ODataEntry;
18
use POData\ObjectModel\ODataFeed;
19
use POData\ObjectModel\ODataLink;
20
use POData\ObjectModel\ODataMediaLink;
21
use POData\ObjectModel\ODataNavigationPropertyInfo;
22
use POData\ObjectModel\ODataProperty;
23
use POData\ObjectModel\ODataPropertyContent;
24
use POData\ObjectModel\ODataTitle;
25
use POData\ObjectModel\ODataURL;
26
use POData\ObjectModel\ODataURLCollection;
27
use POData\Providers\Metadata\IMetadataProvider;
28
use POData\Providers\Metadata\ResourceEntityType;
29
use POData\Providers\Metadata\ResourceProperty;
30
use POData\Providers\Metadata\ResourcePropertyKind;
31
use POData\Providers\Metadata\ResourceSet;
32
use POData\Providers\Metadata\ResourceSetWrapper;
33
use POData\Providers\Metadata\ResourceType;
34
use POData\Providers\Metadata\ResourceTypeKind;
35
use POData\Providers\Metadata\Type\Binary;
36
use POData\Providers\Metadata\Type\Boolean;
37
use POData\Providers\Metadata\Type\DateTime;
38
use POData\Providers\Metadata\Type\IType;
39
use POData\Providers\Metadata\Type\StringType;
40
use POData\Providers\Query\QueryResult;
41
use POData\Providers\Query\QueryType;
42
use POData\UriProcessor\QueryProcessor\ExpandProjectionParser\ExpandedProjectionNode;
43
use POData\UriProcessor\QueryProcessor\ExpandProjectionParser\ProjectionNode;
44
use POData\UriProcessor\QueryProcessor\ExpandProjectionParser\RootProjectionNode;
45
use POData\UriProcessor\QueryProcessor\OrderByParser\InternalOrderByInfo;
46
use POData\UriProcessor\RequestDescription;
47
use POData\UriProcessor\SegmentStack;
48
49
class IronicSerialiser implements IObjectSerialiser
50
{
51
    use SerialiseDepWrapperTrait;
52
    use SerialisePropertyCacheTrait;
53
    use SerialiseNavigationTrait;
54
    use SerialiseLowLevelWritersTrait;
55
    use SerialiseNextPageLinksTrait;
56
    use SerialiseUtilitiesTrait;
57
58
    /**
59
     * Update time to insert into ODataEntry/ODataFeed fields
60
     * @var Carbon
61
     */
62
    private $updated;
63
64
    /**
65
     * Has base URI already been written out during serialisation?
66
     * @var bool
67
     */
68
    private $isBaseWritten = false;
69
70
    /**
71
     * @param IService                $service Reference to the data service instance
72
     * @param RequestDescription|null $request Type instance describing the client submitted request
73
     * @throws \Exception
74
     */
75
    public function __construct(IService $service, RequestDescription $request = null)
76
    {
77
        $this->service = $service;
78
        $this->request = $request;
79
        $this->absoluteServiceUri = $service->getHost()->getAbsoluteServiceUri()->getUrlAsString();
80
        $this->absoluteServiceUriWithSlash = rtrim($this->absoluteServiceUri, '/') . '/';
81
        $this->stack = new SegmentStack($request);
82
        $this->complexTypeInstanceCollection = [];
83
        $this->modelSerialiser = new ModelSerialiser();
84
        $this->updated = Carbon::now();
85
    }
86
87
    /**
88
     * Write a top level entry resource.
89
     *
90
     * @param QueryResult $entryObject Reference to the entry object to be written
91
     *
92
     * @return ODataEntry|null
93
     * @throws InvalidOperationException
94
     * @throws \ReflectionException
95
     * @throws ODataException
96
     */
97
    public function writeTopLevelElement(QueryResult $entryObject)
98
    {
99
        if (!isset($entryObject->results)) {
100
            array_pop($this->lightStack);
101
            return null;
102
        }
103
        $this->checkSingleElementInput($entryObject);
104
105
        $this->loadStackIfEmpty();
106
        $baseURI = $this->isBaseWritten ? null : $this->absoluteServiceUriWithSlash;
107
        $this->isBaseWritten = true;
108
109
        $stackCount = count($this->lightStack);
110
        $topOfStack = $this->lightStack[$stackCount-1];
111
        $payloadClass = get_class($entryObject->results);
0 ignored issues
show
Bug introduced by
It seems like $entryObject->results can also be of type array<mixed,object>; however, parameter $object of get_class() does only seem to accept object, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

111
        $payloadClass = get_class(/** @scrutinizer ignore-type */ $entryObject->results);
Loading history...
112
        /** @var ResourceEntityType $resourceType */
113
        $resourceType = $this->getService()->getProvidersWrapper()->resolveResourceType($topOfStack['type']);
114
115
        // need gubbinz to unpack an abstract resource type
116
        $resourceType = $this->getConcreteTypeFromAbstractType($resourceType, $payloadClass);
117
118
        // make sure we're barking up right tree
119
        if (!$resourceType instanceof ResourceEntityType) {
120
            throw new InvalidOperationException(get_class($resourceType));
121
        }
122
123
        /** @var Model $res */
124
        $res = $entryObject->results;
125
        $targClass = $resourceType->getInstanceType()->getName();
126
        if (!($res instanceof $targClass)) {
127
            $msg = 'Object being serialised not instance of expected class, '
128
                   . $targClass . ', is actually ' . $payloadClass;
129
            throw new InvalidOperationException($msg);
130
        }
131
132
        $this->checkRelationPropertiesCached($targClass, $resourceType);
133
        /** @var ResourceProperty[] $relProp */
134
        $relProp = $this->propertiesCache[$targClass]['rel'];
135
        /** @var ResourceProperty[] $nonRelProp */
136
        $nonRelProp = $this->propertiesCache[$targClass]['nonRel'];
137
138
        $resourceSet = $resourceType->getCustomState();
139
        if (!$resourceSet instanceof ResourceSet) {
140
            throw new InvalidOperationException('');
141
        }
142
        $title = $resourceType->getName();
143
        $type = $resourceType->getFullName();
144
145
        $relativeUri = $this->getEntryInstanceKey(
146
            $res,
147
            $resourceType,
148
            $resourceSet->getName()
149
        );
150
        $absoluteUri = rtrim($this->absoluteServiceUri, '/') . '/' . $relativeUri;
151
152
        /** var $mediaLink ODataMediaLink|null */
153
        $mediaLink = null;
154
        /** var $mediaLinks ODataMediaLink[] */
155
        $mediaLinks = [];
156
        $this->writeMediaData(
157
            $res,
158
            $type,
159
            $relativeUri,
160
            $resourceType,
161
            $mediaLink,
162
            $mediaLinks
163
        );
164
165
        $propertyContent = $this->writePrimitiveProperties($res, $nonRelProp);
166
167
        $links = $this->buildLinksFromRels($entryObject, $relProp, $relativeUri);
168
169
        $odata = new ODataEntry();
170
        $odata->resourceSetName = $resourceSet->getName();
171
        $odata->id = $absoluteUri;
172
        $odata->title = new ODataTitle($title);
173
        $odata->type = new ODataCategory($type);
174
        $odata->propertyContent = $propertyContent;
175
        $odata->isMediaLinkEntry = $resourceType->isMediaLinkEntry();
176
        $odata->editLink = new ODataLink();
177
        $odata->editLink->url = $relativeUri;
178
        $odata->editLink->name = 'edit';
179
        $odata->editLink->title = $title;
180
        $odata->mediaLink = $mediaLink;
181
        $odata->mediaLinks = $mediaLinks;
182
        $odata->links = $links;
183
        $odata->updated = $this->getUpdated()->format(DATE_ATOM);
184
        $odata->baseURI = $baseURI;
185
186
        $newCount = count($this->lightStack);
187
        if ($newCount != $stackCount) {
188
            $msg = 'Should have ' . $stackCount . ' elements in stack, have ' . $newCount . ' elements';
189
            throw new InvalidOperationException($msg);
190
        }
191
        $this->updateLightStack($newCount);
192
        return $odata;
193
    }
194
195
    /**
196
     * Write top level feed element.
197
     *
198
     * @param QueryResult &$entryObjects Array of entry resources to be written
199
     *
200
     * @return ODataFeed
201
     * @throws InvalidOperationException
202
     * @throws ODataException
203
     * @throws \ReflectionException
204
     */
205
    public function writeTopLevelElements(QueryResult &$entryObjects)
206
    {
207
        $this->checkElementsInput($entryObjects);
208
209
        $this->loadStackIfEmpty();
210
211
        $title = $this->getRequest()->getContainerName();
212
        $relativeUri = $this->getRequest()->getIdentifier();
213
        $absoluteUri = $this->getRequest()->getRequestUrl()->getUrlAsString();
214
215
        $selfLink = new ODataLink();
216
        $selfLink->name = 'self';
217
        $selfLink->title = $relativeUri;
218
        $selfLink->url = $relativeUri;
219
220
        $odata = new ODataFeed();
221
        $odata->title = new ODataTitle($title);
222
        $odata->id = $absoluteUri;
223
        $odata->selfLink = $selfLink;
224
        $odata->updated = $this->getUpdated()->format(DATE_ATOM);
225
        $odata->baseURI = $this->isBaseWritten ? null : $this->absoluteServiceUriWithSlash;
226
        $this->isBaseWritten = true;
227
228
        if ($this->getRequest()->queryType == QueryType::ENTITIES_WITH_COUNT()) {
229
            $odata->rowCount = $this->getRequest()->getCountValue();
230
        }
231
        $this->buildEntriesFromElements($entryObjects->results, $odata);
232
233
        $resourceSet = $this->getRequest()->getTargetResourceSetWrapper()->getResourceSet();
234
        $requestTop = $this->getRequest()->getTopOptionCount();
235
        $pageSize = $this->getService()->getConfiguration()->getEntitySetPageSize($resourceSet);
236
        $requestTop = (null === $requestTop) ? $pageSize+1 : $requestTop;
237
238
        if (true === $entryObjects->hasMore && $requestTop > $pageSize) {
239
            $this->buildNextPageLink($entryObjects, $odata);
240
        }
241
242
        return $odata;
243
    }
244
245
    /**
246
     * Write top level url element.
247
     *
248
     * @param QueryResult $entryObject The entry resource whose url to be written
249
     *
250
     * @return ODataURL
251
     * @throws InvalidOperationException
252
     * @throws ODataException
253
     * @throws \ReflectionException
254
     */
255
    public function writeUrlElement(QueryResult $entryObject)
256
    {
257
        $url = new ODataURL();
258
        /** @var Model|null $res */
259
        $res = $entryObject->results;
260
        if (null !== $res) {
261
            $currentResourceType = $this->getCurrentResourceSetWrapper()->getResourceType();
262
            $relativeUri = $this->getEntryInstanceKey(
263
                $res,
264
                $currentResourceType,
265
                $this->getCurrentResourceSetWrapper()->getName()
266
            );
267
268
            $url->url = rtrim($this->absoluteServiceUri, '/') . '/' . $relativeUri;
269
        }
270
271
        return $url;
272
    }
273
274
    /**
275
     * Write top level url collection.
276
     *
277
     * @param QueryResult $entryObjects Array of entry resources whose url to be written
278
     *
279
     * @return ODataURLCollection
280
     * @throws InvalidOperationException
281
     * @throws ODataException
282
     * @throws \ReflectionException
283
     */
284
    public function writeUrlElements(QueryResult $entryObjects)
285
    {
286
        $urls = new ODataURLCollection();
287
        if (!empty($entryObjects->results)) {
288
            $i = 0;
289
            foreach ($entryObjects->results as $entryObject) {
290
                if (!$entryObject instanceof QueryResult) {
291
                    $query = new QueryResult();
292
                    $query->results = $entryObject;
293
                } else {
294
                    $query = $entryObject;
295
                }
296
                $urls->urls[$i] = $this->writeUrlElement($query);
297
                ++$i;
298
            }
299
300
            if ($i > 0 && true === $entryObjects->hasMore) {
301
                $this->buildNextPageLink($entryObjects, $urls);
302
            }
303
        }
304
305
        if ($this->getRequest()->queryType == QueryType::ENTITIES_WITH_COUNT()) {
306
            $urls->count = $this->getRequest()->getCountValue();
307
        }
308
309
        return $urls;
310
    }
311
312
    /**
313
     * Write top level complex resource.
314
     *
315
     * @param QueryResult  &$complexValue The complex object to be written
316
     * @param string       $propertyName  The name of the complex property
317
     * @param ResourceType &$resourceType Describes the type of complex object
318
     *
319
     * @return ODataPropertyContent
320
     * @throws InvalidOperationException
321
     * @throws \ReflectionException
322
     */
323
    public function writeTopLevelComplexObject(QueryResult &$complexValue, $propertyName, ResourceType &$resourceType)
324
    {
325
        $result = $complexValue->results;
326
327
        $propertyContent = new ODataPropertyContent();
328
        $odataProperty = new ODataProperty();
329
        $odataProperty->name = $propertyName;
330
        $odataProperty->typeName = $resourceType->getFullName();
331
        if (null != $result) {
332
            $internalContent = $this->writeComplexValue($resourceType, $result);
0 ignored issues
show
Bug introduced by
It seems like $result can also be of type array<mixed,object>; however, parameter $result of AlgoWeb\PODataLaravel\Se...er::writeComplexValue() does only seem to accept object, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

332
            $internalContent = $this->writeComplexValue($resourceType, /** @scrutinizer ignore-type */ $result);
Loading history...
333
            $odataProperty->value = $internalContent;
334
        }
335
336
        $propertyContent->properties[$propertyName] = $odataProperty;
337
338
        return $propertyContent;
339
    }
340
341
    /**
342
     * Write top level bag resource.
343
     *
344
     * @param QueryResult  &$BagValue     The bag object to be
345
     *                                    written
346
     * @param string       $propertyName  The name of the
347
     *                                    bag property
348
     * @param ResourceType &$resourceType Describes the type of
349
     *                                    bag object
350
     *
351
     * @return ODataPropertyContent
352
     * @throws InvalidOperationException
353
     * @throws \ReflectionException
354
     */
355
    public function writeTopLevelBagObject(QueryResult &$BagValue, $propertyName, ResourceType &$resourceType)
356
    {
357
        $result = $BagValue->results;
358
359
        $propertyContent = new ODataPropertyContent();
360
        $odataProperty = new ODataProperty();
361
        $odataProperty->name = $propertyName;
362
        $odataProperty->typeName = 'Collection(' . $resourceType->getFullName() . ')';
363
        $odataProperty->value = $this->writeBagValue($resourceType, $result);
364
365
        $propertyContent->properties[$propertyName] = $odataProperty;
366
        return $propertyContent;
367
    }
368
369
    /**
370
     * Write top level primitive value.
371
     *
372
     * @param  QueryResult          &$primitiveValue   The primitive value to be
373
     *                                                 written
374
     * @param  ResourceProperty     &$resourceProperty Resource property describing the
375
     *                                                 primitive property to be written
376
     * @return ODataPropertyContent
377
     * @throws InvalidOperationException
378
     * @throws \ReflectionException
379
     */
380
    public function writeTopLevelPrimitive(QueryResult &$primitiveValue, ResourceProperty &$resourceProperty = null)
381
    {
382
        if (null === $resourceProperty) {
383
            throw new InvalidOperationException('Resource property must not be null');
384
        }
385
        $propertyContent = new ODataPropertyContent();
386
387
        $odataProperty = new ODataProperty();
388
        $odataProperty->name = $resourceProperty->getName();
389
        $iType = $resourceProperty->getInstanceType();
390
        if (!$iType instanceof IType) {
391
            throw new InvalidOperationException(get_class($iType));
392
        }
393
        $odataProperty->typeName = $iType->getFullTypeName();
394
        if (null == $primitiveValue->results) {
395
            $odataProperty->value = null;
396
        } else {
397
            $rType = $resourceProperty->getResourceType()->getInstanceType();
398
            if (!$rType instanceof IType) {
399
                throw new InvalidOperationException(get_class($rType));
400
            }
401
            $odataProperty->value = $this->primitiveToString($rType, $primitiveValue->results);
402
        }
403
404
        $propertyContent->properties[$odataProperty->name] = $odataProperty;
405
406
        return $propertyContent;
407
    }
408
409
    /**
410
     * Get update timestamp.
411
     *
412
     * @return Carbon
413
     */
414
    public function getUpdated()
415
    {
416
        return $this->updated;
417
    }
418
419
    /**
420
     * @param $entryObject
421
     * @param $type
422
     * @param $relativeUri
423
     * @param ResourceType $resourceType
424
     * @param ODataMediaLink|null $mediaLink
425
     * @param ODataMediaLink[] $mediaLinks
426
     * @return void
427
     * @throws InvalidOperationException
428
     */
429
    protected function writeMediaData(
430
        $entryObject,
431
        $type,
432
        $relativeUri,
433
        ResourceType $resourceType,
434
        ODataMediaLink &$mediaLink = null,
435
        array &$mediaLinks = []
436
    ) {
437
        $context = $this->getService()->getOperationContext();
438
        $streamProviderWrapper = $this->getService()->getStreamProviderWrapper();
439
        if (null == $streamProviderWrapper) {
440
            throw new InvalidOperationException('Retrieved stream provider must not be null');
441
        }
442
443
        /** @var ODataMediaLink|null $mediaLink */
444
        $mediaLink = null;
445
        if ($resourceType->isMediaLinkEntry()) {
446
            $eTag = $streamProviderWrapper->getStreamETag2($entryObject, null, $context);
0 ignored issues
show
Bug introduced by
The method getStreamETag2() does not exist on POData\Providers\Stream\StreamProviderWrapper. Did you maybe mean getStreamETag()? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

446
            /** @scrutinizer ignore-call */ 
447
            $eTag = $streamProviderWrapper->getStreamETag2($entryObject, null, $context);

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
447
            $mediaLink = new ODataMediaLink($type, '/$value', $relativeUri . '/$value', '*/*', $eTag, 'edit-media');
448
        }
449
        /** @var ODataMediaLink[] $mediaLinks */
450
        $mediaLinks = [];
451
        if ($resourceType->hasNamedStream()) {
452
            $namedStreams = $resourceType->getAllNamedStreams();
453
            foreach ($namedStreams as $streamTitle => $resourceStreamInfo) {
454
                $readUri = $streamProviderWrapper->getReadStreamUri2(
0 ignored issues
show
Bug introduced by
The method getReadStreamUri2() does not exist on POData\Providers\Stream\StreamProviderWrapper. Did you maybe mean getReadStreamUri()? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

454
                /** @scrutinizer ignore-call */ 
455
                $readUri = $streamProviderWrapper->getReadStreamUri2(

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
455
                    $entryObject,
456
                    $resourceStreamInfo,
457
                    $context,
458
                    $relativeUri
459
                );
460
                $mediaContentType = $streamProviderWrapper->getStreamContentType2(
0 ignored issues
show
Bug introduced by
The method getStreamContentType2() does not exist on POData\Providers\Stream\StreamProviderWrapper. Did you maybe mean getStreamContentType()? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

460
                /** @scrutinizer ignore-call */ 
461
                $mediaContentType = $streamProviderWrapper->getStreamContentType2(

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
461
                    $entryObject,
462
                    $resourceStreamInfo,
463
                    $context
464
                );
465
                $eTag = $streamProviderWrapper->getStreamETag2(
466
                    $entryObject,
467
                    $resourceStreamInfo,
468
                    $context
469
                );
470
471
                $nuLink = new ODataMediaLink($streamTitle, $readUri, $readUri, $mediaContentType, $eTag);
472
                $mediaLinks[] = $nuLink;
473
            }
474
        }
475
    }
476
477
    /**
478
     * @param QueryResult $entryObject
479
     * @param ResourceProperty $prop
480
     * @param $nuLink
481
     * @param $propKind
482
     * @param $propName
483
     * @throws InvalidOperationException
484
     * @throws ODataException
485
     * @throws \ReflectionException
486
     */
487
    private function expandNavigationProperty(
488
        QueryResult $entryObject,
489
        ResourceProperty $prop,
490
        $nuLink,
491
        $propKind,
492
        $propName
493
    ) {
494
        $nextName = $prop->getResourceType()->getName();
495
        $nuLink->isExpanded = true;
496
        $value = $entryObject->results->$propName;
497
        $isCollection = ResourcePropertyKind::RESOURCESET_REFERENCE == $propKind;
498
        $nuLink->isCollection = $isCollection;
499
500
        if (is_array($value)) {
501
            if (1 == count($value) && !$isCollection) {
502
                $value = $value[0];
503
            } else {
504
                $value = collect($value);
505
            }
506
        }
507
508
        $result = new QueryResult();
509
        $result->results = $value;
510
        $nullResult = null === $value;
511
        $isSingleton = $value instanceof Model;
512
        $resultCount = $nullResult ? 0 : ($isSingleton ? 1 : $value->count());
513
514
        if (0 < $resultCount) {
515
            $newStackLine = ['type' => $nextName, 'prop' => $propName, 'count' => $resultCount];
516
            array_push($this->lightStack, $newStackLine);
517
            if (!$isCollection) {
518
                $nuLink->type = 'application/atom+xml;type=entry';
519
                $expandedResult = $this->writeTopLevelElement($result);
520
            } else {
521
                $nuLink->type = 'application/atom+xml;type=feed';
522
                $expandedResult = $this->writeTopLevelElements($result);
523
            }
524
            $nuLink->expandedResult = $expandedResult;
525
        } else {
526
            $type = $this->getService()->getProvidersWrapper()->resolveResourceType($nextName);
527
            if (!$isCollection) {
528
                $result = new ODataEntry();
529
                $result->resourceSetName = $type->getName();
530
            } else {
531
                $result = new ODataFeed();
532
                $result->selfLink = new ODataLink();
533
                $result->selfLink->name = ODataConstants::ATOM_SELF_RELATION_ATTRIBUTE_VALUE;
534
            }
535
            $nuLink->expandedResult = $result;
536
        }
537
        if (isset($nuLink->expandedResult->selfLink)) {
538
            $nuLink->expandedResult->selfLink->title = $propName;
539
            $nuLink->expandedResult->selfLink->url = $nuLink->url;
540
            $nuLink->expandedResult->title = new ODataTitle($propName);
541
            $nuLink->expandedResult->id = rtrim($this->absoluteServiceUri, '/') . '/' . $nuLink->url;
542
        }
543
        if (!isset($nuLink->expandedResult)) {
544
            throw new InvalidOperationException('');
545
        }
546
    }
547
548
    /**
549
     * @param QueryResult $entryObject
550
     * @param array $relProp
551
     * @param $relativeUri
552
     * @return array
553
     * @throws InvalidOperationException
554
     * @throws ODataException
555
     * @throws \ReflectionException
556
     */
557
    protected function buildLinksFromRels(QueryResult $entryObject, array $relProp, $relativeUri)
558
    {
559
        $links = [];
560
        foreach ($relProp as $prop) {
561
            $nuLink = new ODataLink();
562
            $propKind = $prop->getKind();
563
564
            if (!(ResourcePropertyKind::RESOURCESET_REFERENCE == $propKind
565
                  || ResourcePropertyKind::RESOURCE_REFERENCE == $propKind)) {
566
                $msg = '$propKind != ResourcePropertyKind::RESOURCESET_REFERENCE &&'
567
                       . ' $propKind != ResourcePropertyKind::RESOURCE_REFERENCE';
568
                throw new InvalidOperationException($msg);
569
            }
570
            $propTail = ResourcePropertyKind::RESOURCE_REFERENCE == $propKind ? 'entry' : 'feed';
571
            $propType = 'application/atom+xml;type=' . $propTail;
572
            $propName = $prop->getName();
573
            $nuLink->title = $propName;
574
            $nuLink->name = ODataConstants::ODATA_RELATED_NAMESPACE . $propName;
575
            $nuLink->url = $relativeUri . '/' . $propName;
576
            $nuLink->type = $propType;
577
            $nuLink->isExpanded = false;
578
            $nuLink->isCollection = 'feed' === $propTail;
579
580
            $shouldExpand = $this->shouldExpandSegment($propName);
581
582
            $navProp = new ODataNavigationPropertyInfo($prop, $shouldExpand);
583
            if ($navProp->expanded) {
584
                $this->expandNavigationProperty($entryObject, $prop, $nuLink, $propKind, $propName);
585
            }
586
            $nuLink->isExpanded = isset($nuLink->expandedResult);
587
            if (null === $nuLink->isCollection) {
588
                throw new InvalidOperationException('');
589
            }
590
591
            $links[] = $nuLink;
592
        }
593
        return $links;
594
    }
595
596
    /**
597
     * @param $res
598
     * @param ODataFeed $odata
599
     * @throws InvalidOperationException
600
     * @throws ODataException
601
     * @throws \ReflectionException
602
     */
603
    protected function buildEntriesFromElements($res, ODataFeed $odata)
604
    {
605
        foreach ($res as $entry) {
606
            if (!$entry instanceof QueryResult) {
607
                $query = new QueryResult();
608
                $query->results = $entry;
609
            } else {
610
                $query = $entry;
611
            }
612
            $odata->entries[] = $this->writeTopLevelElement($query);
613
        }
614
    }
615
}
616