Test Failed
Push — master ( 125bb1...2a5ec2 )
by Alex
03:17
created

IronicSerialiser::writeTopLevelElements()   C

Complexity

Conditions 7
Paths 24

Size

Total Lines 51
Code Lines 37

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 51
rs 6.9743
cc 7
eloc 37
nc 24
nop 1

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 POData\Common\Messages;
6
use POData\Common\ODataConstants;
7
use POData\Common\ODataException;
8
use POData\IService;
9
use POData\ObjectModel\IObjectSerialiser;
10
use POData\ObjectModel\ODataEntry;
11
use POData\ObjectModel\ODataFeed;
12
use POData\ObjectModel\ODataLink;
13
use POData\ObjectModel\ODataMediaLink;
14
use POData\ObjectModel\ODataNavigationPropertyInfo;
15
use POData\ObjectModel\ODataProperty;
16
use POData\ObjectModel\ODataPropertyContent;
17
use POData\ObjectModel\ODataURL;
18
use POData\ObjectModel\ODataURLCollection;
19
use POData\Providers\Metadata\ResourceEntityType;
20
use POData\Providers\Metadata\ResourceProperty;
21
use POData\Providers\Metadata\ResourcePropertyKind;
22
use POData\Providers\Metadata\ResourceSet;
23
use POData\Providers\Metadata\ResourceSetWrapper;
24
use POData\Providers\Metadata\ResourceType;
25
use POData\Providers\Metadata\Type\Binary;
26
use POData\Providers\Metadata\Type\Boolean;
27
use POData\Providers\Metadata\Type\DateTime;
28
use POData\Providers\Metadata\Type\IType;
29
use POData\Providers\Metadata\Type\StringType;
30
use POData\Providers\Query\QueryResult;
31
use POData\Providers\Query\QueryType;
32
use POData\UriProcessor\QueryProcessor\ExpandProjectionParser\ExpandedProjectionNode;
33
use POData\UriProcessor\QueryProcessor\ExpandProjectionParser\ProjectionNode;
34
use POData\UriProcessor\QueryProcessor\OrderByParser\InternalOrderByInfo;
35
use POData\UriProcessor\RequestDescription;
36
use POData\UriProcessor\SegmentStack;
37
38
class IronicSerialiser implements IObjectSerialiser
39
{
40
    /**
41
     * The service implementation.
42
     *
43
     * @var IService
44
     */
45
    protected $service;
46
47
    /**
48
     * Request description instance describes OData request the
49
     * the client has submitted and result of the request.
50
     *
51
     * @var RequestDescription
52
     */
53
    protected $request;
54
55
    /**
56
     * Collection of complex type instances used for cycle detection.
57
     *
58
     * @var array
59
     */
60
    protected $complexTypeInstanceCollection;
61
62
    /**
63
     * Absolute service Uri.
64
     *
65
     * @var string
66
     */
67
    protected $absoluteServiceUri;
68
69
    /**
70
     * Absolute service Uri with slash.
71
     *
72
     * @var string
73
     */
74
    protected $absoluteServiceUriWithSlash;
75
76
    /**
77
     * Holds reference to segment stack being processed.
78
     *
79
     * @var SegmentStack
80
     */
81
    protected $stack;
82
83
    /**
84
     * Lightweight stack tracking for recursive descent fill
85
     */
86
    private $lightStack = [];
87
88
    private $modelSerialiser;
89
90
    /**
91
     * @param IService           $service Reference to the data service instance
92
     * @param RequestDescription $request Type instance describing the client submitted request
93
     */
94
    public function __construct(IService $service, RequestDescription $request = null)
95
    {
96
        $this->service = $service;
97
        $this->request = $request;
98
        $this->absoluteServiceUri = $service->getHost()->getAbsoluteServiceUri()->getUrlAsString();
99
        $this->absoluteServiceUriWithSlash = rtrim($this->absoluteServiceUri, '/') . '/';
100
        $this->stack = new SegmentStack($request);
101
        $this->complexTypeInstanceCollection = [];
102
        $this->modelSerialiser = new ModelSerialiser();
103
    }
104
105
    /**
106
     * Write a top level entry resource.
107
     *
108
     * @param mixed $entryObject Reference to the entry object to be written
109
     *
110
     * @return ODataEntry
111
     */
112
    public function writeTopLevelElement(QueryResult $entryObject)
113
    {
114
        if (!isset($entryObject->results)) {
115
            array_pop($this->lightStack);
116
            return null;
117
        }
118
119
        $this->loadStackIfEmpty();
120
121
        $stackCount = count($this->lightStack);
122
        $topOfStack = $this->lightStack[$stackCount-1];
123
        $resourceType = $this->getService()->getProvidersWrapper()->resolveResourceType($topOfStack[0]);
124
        $rawProp = $resourceType->getAllProperties();
125
        $relProp = [];
126
        $nonRelProp = [];
127
        foreach ($rawProp as $prop) {
128
            if ($prop->getResourceType() instanceof ResourceEntityType) {
129
                $relProp[] = $prop;
130
            } else {
131
                $nonRelProp[$prop->getName()] = $prop;
132
            }
133
        }
134
135
        $resourceSet = $resourceType->getCustomState();
136
        assert($resourceSet instanceof ResourceSet);
137
        $title = $resourceType->getName();
138
        $type = $resourceType->getFullName();
139
140
        $relativeUri = $this->getEntryInstanceKey(
141
            $entryObject->results,
142
            $resourceType,
143
            $resourceSet->getName()
144
        );
145
        $absoluteUri = rtrim($this->absoluteServiceUri, '/') . '/' . $relativeUri;
146
147
        list($mediaLink, $mediaLinks) = $this->writeMediaData($entryObject->results, $type, $relativeUri, $resourceType);
148
149
        $propertyContent = $this->writePrimitiveProperties($entryObject->results, $nonRelProp);
150
151
        $links = [];
152
        foreach ($relProp as $prop) {
153
            $nuLink = new ODataLink();
154
            $propKind = $prop->getKind();
155
156
            assert(
157
                ResourcePropertyKind::RESOURCESET_REFERENCE == $propKind
158
                || ResourcePropertyKind::RESOURCE_REFERENCE == $propKind,
159
                '$propKind != ResourcePropertyKind::RESOURCESET_REFERENCE &&'
160
                .' $propKind != ResourcePropertyKind::RESOURCE_REFERENCE'
161
            );
162
            $propTail = ResourcePropertyKind::RESOURCE_REFERENCE == $propKind ? 'entry' : 'feed';
163
            $propType = 'application/atom+xml;type='.$propTail;
164
            $propName = $prop->getName();
165
            $nuLink->title = $propName;
166
            $nuLink->name = ODataConstants::ODATA_RELATED_NAMESPACE . $propName;
167
            $nuLink->url = $relativeUri . '/' . $propName;
168
            $nuLink->type = $propType;
169
170
            $navProp = new ODataNavigationPropertyInfo($prop, $this->shouldExpandSegment($propName));
171
            if ($navProp->expanded) {
172
                $this->expandNavigationProperty($entryObject, $prop, $nuLink, $propKind, $propName);
173
            }
174
175
            $links[] = $nuLink;
176
        }
177
178
        $odata = new ODataEntry();
179
        $odata->resourceSetName = $resourceSet->getName();
180
        $odata->id = $absoluteUri;
181
        $odata->title = $title;
182
        $odata->type = $type;
183
        $odata->propertyContent = $propertyContent;
184
        $odata->isMediaLinkEntry = $resourceType->isMediaLinkEntry();
185
        $odata->editLink = $relativeUri;
186
        $odata->mediaLink = $mediaLink;
187
        $odata->mediaLinks = $mediaLinks;
188
        $odata->links = $links;
189
190
        $newCount = count($this->lightStack);
191
        assert($newCount == $stackCount, "Should have $stackCount elements in stack, have $newCount elements");
192
        array_pop($this->lightStack);
193
        return $odata;
194
    }
195
196
    /**
197
     * Write top level feed element.
198
     *
199
     * @param array &$entryObjects Array of entry resources to be written
200
     *
201
     * @return ODataFeed
202
     */
203
    public function writeTopLevelElements(QueryResult &$entryObjects)
204
    {
205
        assert(is_array($entryObjects->results), '!is_array($entryObjects->results)');
206
207
        $this->loadStackIfEmpty();
208
        $setName = $this->getRequest()->getTargetResourceSetWrapper()->getName();
209
210
        $title = $this->getRequest()->getContainerName();
211
        $relativeUri = $this->getRequest()->getIdentifier();
212
        $absoluteUri = $this->getRequest()->getRequestUrl()->getUrlAsString();
213
214
        $selfLink = new ODataLink();
215
        $selfLink->name = 'self';
216
        $selfLink->title = $relativeUri;
217
        $selfLink->url = $relativeUri;
218
219
        $odata = new ODataFeed();
220
        $odata->title = $title;
221
        $odata->id = $absoluteUri;
222
        $odata->selfLink = $selfLink;
223
224
        if ($this->getRequest()->queryType == QueryType::ENTITIES_WITH_COUNT()) {
225
            $odata->rowCount = $this->getRequest()->getCountValue();
226
        }
227
        foreach ($entryObjects->results as $entry) {
228
            if (!$entry instanceof QueryResult) {
229
                $query = new QueryResult();
230
                $query->results = $entry;
231
            } else {
232
                $query = $entry;
233
            }
234
            $odata->entries[] = $this->writeTopLevelElement($query);
235
        }
236
237
        $resourceSet = $this->getRequest()->getTargetResourceSetWrapper()->getResourceSet();
238
        $requestTop = $this->getRequest()->getTopOptionCount();
239
        $pageSize = $this->getService()->getConfiguration()->getEntitySetPageSize($resourceSet);
240
        $requestTop = (null == $requestTop) ? $pageSize + 1 : $requestTop;
0 ignored issues
show
Bug Best Practice introduced by
It seems like you are loosely comparing $requestTop of type integer|null against null; this is ambiguous if the integer can be zero. Consider using a strict comparison === instead.
Loading history...
241
242
        if (true === $entryObjects->hasMore && $requestTop > $pageSize) {
0 ignored issues
show
Bug introduced by
The property hasMore does not seem to exist in POData\Providers\Query\QueryResult.

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
243
            $stackSegment = $setName;
244
            $lastObject = end($entryObjects->results);
245
            $segment = $this->getNextLinkUri($lastObject, $absoluteUri);
246
            $nextLink = new ODataLink();
247
            $nextLink->name = ODataConstants::ATOM_LINK_NEXT_ATTRIBUTE_STRING;
248
            $nextLink->url = rtrim($this->absoluteServiceUri, '/') . '/' . $stackSegment . $segment;
249
            $odata->nextPageLink = $nextLink;
250
        }
251
252
        return $odata;
253
    }
254
255
    /**
256
     * Write top level url element.
257
     *
258
     * @param mixed $entryObject The entry resource whose url to be written
259
     *
260
     * @return ODataURL
261
     */
262
    public function writeUrlElement(QueryResult $entryObject)
263
    {
264
        $url = new ODataURL();
265
        if (!is_null($entryObject->results)) {
266
            $currentResourceType = $this->getCurrentResourceSetWrapper()->getResourceType();
267
            $relativeUri = $this->getEntryInstanceKey(
268
                $entryObject->results,
269
                $currentResourceType,
270
                $this->getCurrentResourceSetWrapper()->getName()
271
            );
272
273
            $url->url = rtrim($this->absoluteServiceUri, '/') . '/' . $relativeUri;
274
        }
275
276
        return $url;
277
    }
278
279
    /**
280
     * Write top level url collection.
281
     *
282
     * @param array $entryObjects Array of entry resources
283
     *                            whose url to be written
284
     *
285
     * @return ODataURLCollection
286
     */
287
    public function writeUrlElements(QueryResult $entryObjects)
288
    {
289
        $urls = new ODataURLCollection();
290
        if (!empty($entryObjects->results)) {
291
            $i = 0;
292
            foreach ($entryObjects->results as $entryObject) {
293
                $urls->urls[$i] = $this->writeUrlElement($entryObject);
294
                ++$i;
295
            }
296
297
            if ($i > 0 && true === $entryObjects->hasMore) {
0 ignored issues
show
Bug introduced by
The property hasMore does not seem to exist in POData\Providers\Query\QueryResult.

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
298
                $stackSegment = $this->getRequest()->getTargetResourceSetWrapper()->getName();
299
                $lastObject = end($entryObjects->results);
300
                $segment = $this->getNextLinkUri($lastObject, $this->getRequest()->getRequestUrl()->getUrlAsString());
301
                $nextLink = new ODataLink();
302
                $nextLink->name = ODataConstants::ATOM_LINK_NEXT_ATTRIBUTE_STRING;
303
                $nextLink->url = rtrim($this->absoluteServiceUri, '/') . '/' . $stackSegment . $segment;
304
                $urls->nextPageLink = $nextLink;
305
            }
306
        }
307
308
        if ($this->getRequest()->queryType == QueryType::ENTITIES_WITH_COUNT()) {
309
            $urls->count = $this->getRequest()->getCountValue();
310
        }
311
312
        return $urls;
313
    }
314
315
    /**
316
     * Write top level complex resource.
317
     *
318
     * @param mixed &$complexValue The complex object to be
319
     *                                    written
320
     * @param string $propertyName The name of the
321
     *                                    complex property
322
     * @param ResourceType &$resourceType Describes the type of
323
     *                                    complex object
324
     *
325
     * @return ODataPropertyContent
326
     * @codeCoverageIgnore
327
     */
328
    public function writeTopLevelComplexObject(QueryResult &$complexValue, $propertyName, ResourceType &$resourceType)
329
    {
330
        // TODO: Figure out if we need to bother implementing this
331
    }
332
333
    /**
334
     * Write top level bag resource.
335
     *
336
     * @param mixed &$BagValue The bag object to be
337
     *                                    written
338
     * @param string $propertyName The name of the
339
     *                                    bag property
340
     * @param ResourceType &$resourceType Describes the type of
341
     *                                    bag object
342
     * @codeCoverageIgnore
343
     * @return ODataPropertyContent
344
     */
345
    public function writeTopLevelBagObject(QueryResult &$BagValue, $propertyName, ResourceType &$resourceType)
346
    {
347
        // TODO: Figure out if we need to bother implementing this
348
    }
349
350
    /**
351
     * Write top level primitive value.
352
     *
353
     * @param mixed &$primitiveValue The primitve value to be
354
     *                                            written
355
     * @param ResourceProperty &$resourceProperty Resource property
356
     *                                            describing the
357
     *                                            primitive property
358
     *                                            to be written
359
     * @codeCoverageIgnore
360
     * @return ODataPropertyContent
361
     */
362
    public function writeTopLevelPrimitive(QueryResult &$primitiveValue, ResourceProperty &$resourceProperty = null)
363
    {
364
        // TODO: Figure out if we need to bother implementing this
365
    }
366
367
    /**
368
     * Gets reference to the request submitted by client.
369
     *
370
     * @return RequestDescription
371
     */
372
    public function getRequest()
373
    {
374
        assert(null != $this->request, 'Request not yet set');
375
376
        return $this->request;
377
    }
378
379
    /**
380
     * Sets reference to the request submitted by client.
381
     *
382
     * @param RequestDescription $request
383
     */
384
    public function setRequest(RequestDescription $request)
385
    {
386
        $this->request = $request;
387
        $this->stack->setRequest($request);
388
    }
389
390
    /**
391
     * Gets the data service instance.
392
     *
393
     * @return IService
394
     */
395
    public function getService()
396
    {
397
        return $this->service;
398
    }
399
400
    /**
401
     * Gets the segment stack instance.
402
     *
403
     * @return SegmentStack
404
     */
405
    public function getStack()
406
    {
407
        return $this->stack;
408
    }
409
410
    protected function getEntryInstanceKey($entityInstance, ResourceType $resourceType, $containerName)
411
    {
412
        $typeName = $resourceType->getName();
413
        $keyProperties = $resourceType->getKeyProperties();
414
        assert(count($keyProperties) != 0, 'count($keyProperties) == 0');
415
        $keyString = $containerName . '(';
416
        $comma = null;
417
        foreach ($keyProperties as $keyName => $resourceProperty) {
418
            $keyType = $resourceProperty->getInstanceType();
419
            assert($keyType instanceof IType, '$keyType not instanceof IType');
420
            $keyName = $resourceProperty->getName();
421
            $keyValue = $entityInstance->$keyName;
422
            if (!isset($keyValue)) {
423
                throw ODataException::createInternalServerError(
424
                    Messages::badQueryNullKeysAreNotSupported($typeName, $keyName)
425
                );
426
            }
427
428
            $keyValue = $keyType->convertToOData($keyValue);
429
            $keyString .= $comma . $keyName . '=' . $keyValue;
430
            $comma = ',';
431
        }
432
433
        $keyString .= ')';
434
435
        return $keyString;
436
    }
437
438
    /**
439
     * @param $entryObject
440
     * @param $type
441
     * @param $relativeUri
442
     * @param $resourceType
443
     * @return array
444
     */
445
    protected function writeMediaData($entryObject, $type, $relativeUri, ResourceType $resourceType)
446
    {
447
        $context = $this->getService()->getOperationContext();
448
        $streamProviderWrapper = $this->getService()->getStreamProviderWrapper();
449
        assert(null != $streamProviderWrapper, "Retrieved stream provider must not be null");
450
451
        $mediaLink = null;
452
        if ($resourceType->isMediaLinkEntry()) {
453
            $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()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
454
            $mediaLink = new ODataMediaLink($type, '/$value', $relativeUri . '/$value', '*/*', $eTag);
455
        }
456
        $mediaLinks = [];
457
        if ($resourceType->hasNamedStream()) {
458
            $namedStreams = $resourceType->getAllNamedStreams();
459
            foreach ($namedStreams as $streamTitle => $resourceStreamInfo) {
460
                $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 getReadStream()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
461
                    $entryObject,
462
                    $resourceStreamInfo,
463
                    $context,
464
                    $relativeUri
465
                );
466
                $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()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
467
                    $entryObject,
468
                    $resourceStreamInfo,
469
                    $context
470
                );
471
                $eTag = $streamProviderWrapper->getStreamETag2(
0 ignored issues
show
Bug introduced by
The method getStreamETag2() does not exist on POData\Providers\Stream\StreamProviderWrapper. Did you maybe mean getStreamETag()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
472
                    $entryObject,
473
                    $resourceStreamInfo,
474
                    $context
475
                );
476
477
                $nuLink = new ODataMediaLink($streamTitle, $readUri, $readUri, $mediaContentType, $eTag);
478
                $mediaLinks[] = $nuLink;
479
            }
480
        }
481
        return [$mediaLink, $mediaLinks];
482
    }
483
484
    /**
485
     * Gets collection of projection nodes under the current node.
486
     *
487
     * @return ProjectionNode[]|ExpandedProjectionNode[]|null List of nodes
488
     *                                                        describing projections for the current segment, If this method returns
489
     *                                                        null it means no projections are to be applied and the entire resource
490
     *                                                        for the current segment should be serialized, If it returns non-null
491
     *                                                        only the properties described by the returned projection segments should
492
     *                                                        be serialized
493
     */
494
    protected function getProjectionNodes()
495
    {
496
        $expandedProjectionNode = $this->getCurrentExpandedProjectionNode();
497
        if (is_null($expandedProjectionNode) || $expandedProjectionNode->canSelectAllProperties()) {
498
            return null;
499
        }
500
501
        return $expandedProjectionNode->getChildNodes();
502
    }
503
504
    /**
505
     * Find a 'ExpandedProjectionNode' instance in the projection tree
506
     * which describes the current segment.
507
     *
508
     * @return ExpandedProjectionNode|null
509
     */
510
    protected function getCurrentExpandedProjectionNode()
511
    {
512
        $expandedProjectionNode = $this->getRequest()->getRootProjectionNode();
513
        if (is_null($expandedProjectionNode)) {
514
            return null;
515
        } else {
516
            $segmentNames = $this->getStack()->getSegmentNames();
517
            $depth = count($segmentNames);
518
            // $depth == 1 means serialization of root entry
519
            //(the resource identified by resource path) is going on,
520
            //so control won't get into the below for loop.
521
            //we will directly return the root node,
522
            //which is 'ExpandedProjectionNode'
523
            // for resource identified by resource path.
524
            if (0 != $depth) {
525
                for ($i = 1; $i < $depth; ++$i) {
526
                    $expandedProjectionNode = $expandedProjectionNode->findNode($segmentNames[$i]);
527
                    assert(!is_null($expandedProjectionNode), 'is_null($expandedProjectionNode)');
528
                    assert(
529
                        $expandedProjectionNode instanceof ExpandedProjectionNode,
530
                        '$expandedProjectionNode not instanceof ExpandedProjectionNode'
531
                    );
532
                }
533
            }
534
        }
535
536
        return $expandedProjectionNode;
537
    }
538
539
    /**
540
     * Check whether to expand a navigation property or not.
541
     *
542
     * @param string $navigationPropertyName Name of naviagtion property in question
543
     *
544
     * @return bool True if the given navigation should be
545
     *              explanded otherwise false
546
     */
547
    protected function shouldExpandSegment($navigationPropertyName)
548
    {
549
        $expandedProjectionNode = $this->getCurrentExpandedProjectionNode();
550
        if (is_null($expandedProjectionNode)) {
551
            return false;
552
        }
553
554
        $expandedProjectionNode = $expandedProjectionNode->findNode($navigationPropertyName);
555
556
        // null is a valid input to an instanceof call as of PHP 5.6 - will always return false
557
        return $expandedProjectionNode instanceof ExpandedProjectionNode;
558
    }
559
560
    /**
561
     * Wheter next link is needed for the current resource set (feed)
562
     * being serialized.
563
     *
564
     * @param int $resultSetCount Number of entries in the current
565
     *                            resource set
566
     *
567
     * @return bool true if the feed must have a next page link
568
     */
569
    protected function needNextPageLink($resultSetCount)
570
    {
571
        $currentResourceSet = $this->getCurrentResourceSetWrapper();
572
        $recursionLevel = count($this->getStack()->getSegmentNames());
573
        $pageSize = $currentResourceSet->getResourceSetPageSize();
574
575
        if (1 == $recursionLevel) {
576
            //presence of $top option affect next link for root container
577
            $topValueCount = $this->getRequest()->getTopOptionCount();
578
            if (!is_null($topValueCount) && ($topValueCount <= $pageSize)) {
579
                return false;
580
            }
581
        }
582
        return $resultSetCount == $pageSize;
583
    }
584
585
    /**
586
     * Resource set wrapper for the resource being serialized.
587
     *
588
     * @return ResourceSetWrapper
589
     */
590
    protected function getCurrentResourceSetWrapper()
591
    {
592
        $segmentWrappers = $this->getStack()->getSegmentWrappers();
593
        $count = count($segmentWrappers);
594
595
        return 0 == $count ? $this->getRequest()->getTargetResourceSetWrapper() : $segmentWrappers[$count - 1];
596
    }
597
598
    /**
599
     * Get next page link from the given entity instance.
600
     *
601
     * @param mixed  &$lastObject Last object serialized to be
602
     *                            used for generating $skiptoken
603
     * @param string $absoluteUri Absolute response URI
604
     *
605
     * @return string for the link for next page
606
     */
607
    protected function getNextLinkUri(&$lastObject, $absoluteUri)
0 ignored issues
show
Unused Code introduced by
The parameter $absoluteUri is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
608
    {
609
        $currentExpandedProjectionNode = $this->getCurrentExpandedProjectionNode();
610
        $internalOrderByInfo = $currentExpandedProjectionNode->getInternalOrderByInfo();
611
        assert(null != $internalOrderByInfo);
612
        assert(is_object($internalOrderByInfo));
613
        assert($internalOrderByInfo instanceof InternalOrderByInfo, get_class($internalOrderByInfo));
614
        $numSegments = count($internalOrderByInfo->getOrderByPathSegments());
615
        $queryParameterString = $this->getNextPageLinkQueryParametersForRootResourceSet();
616
617
        $skipToken = $internalOrderByInfo->buildSkipTokenValue($lastObject);
618
        assert(!is_null($skipToken), '!is_null($skipToken)');
619
        $token = (1 < $numSegments) ? '$skiptoken=' : '$skip=';
620
        $skipToken = '?'.$queryParameterString.$token.$skipToken;
621
622
        return $skipToken;
623
    }
624
625
    /**
626
     * Builds the string corresponding to query parameters for top level results
627
     * (result set identified by the resource path) to be put in next page link.
628
     *
629
     * @return string|null string representing the query parameters in the URI
630
     *                     query parameter format, NULL if there
631
     *                     is no query parameters
632
     *                     required for the next link of top level result set
633
     */
634
    protected function getNextPageLinkQueryParametersForRootResourceSet()
635
    {
636
        $queryParameterString = null;
637
        foreach ([ODataConstants::HTTPQUERY_STRING_FILTER,
638
                     ODataConstants::HTTPQUERY_STRING_EXPAND,
639
                     ODataConstants::HTTPQUERY_STRING_ORDERBY,
640
                     ODataConstants::HTTPQUERY_STRING_INLINECOUNT,
641
                     ODataConstants::HTTPQUERY_STRING_SELECT, ] as $queryOption) {
642
            $value = $this->getService()->getHost()->getQueryStringItem($queryOption);
643
            if (!is_null($value)) {
644
                if (!is_null($queryParameterString)) {
645
                    $queryParameterString = $queryParameterString . '&';
646
                }
647
648
                $queryParameterString .= $queryOption . '=' . $value;
649
            }
650
        }
651
652
        $topCountValue = $this->getRequest()->getTopOptionCount();
653
        if (!is_null($topCountValue)) {
654
            $remainingCount = $topCountValue - $this->getRequest()->getTopCount();
655
            if (0 < $remainingCount) {
656
                if (!is_null($queryParameterString)) {
657
                    $queryParameterString .= '&';
658
                }
659
660
                $queryParameterString .= ODataConstants::HTTPQUERY_STRING_TOP . '=' . $remainingCount;
661
            }
662
        }
663
664
        if (!is_null($queryParameterString)) {
665
            $queryParameterString .= '&';
666
        }
667
668
        return $queryParameterString;
669
    }
670
671
    private function loadStackIfEmpty()
672
    {
673
        if (0 == count($this->lightStack)) {
674
            $typeName = $this->getRequest()->getTargetResourceType()->getName();
675
            array_push($this->lightStack, [$typeName, $typeName]);
676
        }
677
    }
678
679
    /**
680
     * Convert the given primitive value to string.
681
     * Note: This method will not handle null primitive value.
682
     *
683
     * @param IType &$primitiveResourceType        Type of the primitive property
684
     *                                             whose value need to be converted
685
     * @param mixed        $primitiveValue         Primitive value to convert
686
     *
687
     * @return string
688
     */
689
    private function primitiveToString(IType &$type, $primitiveValue)
690
    {
691
        if ($type instanceof Boolean) {
692
            $stringValue = (true === $primitiveValue) ? 'true' : 'false';
693
        } elseif ($type instanceof Binary) {
694
            $stringValue = base64_encode($primitiveValue);
695
        } elseif ($type instanceof DateTime && $primitiveValue instanceof \DateTime) {
696
            $stringValue = $primitiveValue->format(\DateTime::ATOM);
697
        } elseif ($type instanceof StringType) {
698
            $stringValue = utf8_encode($primitiveValue);
699
        } else {
700
            $stringValue = strval($primitiveValue);
701
        }
702
703
        return $stringValue;
704
    }
705
706
    /**
707
     * @param $entryObject
708
     * @param $nonRelProp
709
     * @return ODataPropertyContent
710
     */
711
    private function writePrimitiveProperties($entryObject, $nonRelProp)
712
    {
713
        $propertyContent = new ODataPropertyContent();
714
        $cereal = $this->modelSerialiser->bulkSerialise($entryObject);
715
        foreach ($cereal as $corn => $flake) {
716
            if (!array_key_exists($corn, $nonRelProp)) {
717
                continue;
718
            }
719
            $corn = strval($corn);
720
            $rType = $nonRelProp[$corn]->getResourceType()->getInstanceType();
721
            $subProp = new ODataProperty();
722
            $subProp->name = $corn;
723
            $subProp->value = isset($flake) ? $this->primitiveToString($rType, $flake) : null;
724
            $subProp->typeName = $nonRelProp[$corn]->getResourceType()->getFullName();
725
            $propertyContent->properties[] = $subProp;
726
        }
727
        return $propertyContent;
728
    }
729
730
    /**
731
     * @param $entryObject
732
     * @param $prop
733
     * @param $nuLink
734
     * @param $propKind
735
     * @param $propName
736
     */
737
    private function expandNavigationProperty(QueryResult $entryObject, $prop, $nuLink, $propKind, $propName)
738
    {
739
        $nextName = $prop->getResourceType()->getName();
740
        $nuLink->isExpanded = true;
741
        $isCollection = ResourcePropertyKind::RESOURCESET_REFERENCE == $propKind;
742
        $nuLink->isCollection = $isCollection;
743
        $value = $entryObject->results->$propName;
744
        $result = new QueryResult();
745
        $result->results = $value;
746
        array_push($this->lightStack, [$nextName, $propName]);
747
        if (!$isCollection) {
748
            $expandedResult = $this->writeTopLevelElement($result);
749
        } else {
750
            $expandedResult = $this->writeTopLevelElements($result);
751
        }
752
        $nuLink->expandedResult = $expandedResult;
753
        if (!isset($nuLink->expandedResult)) {
754
            $nuLink->isCollection = null;
755
            $nuLink->isExpanded = null;
756
        } else {
757
            if (isset($nuLink->expandedResult->selfLink)) {
758
                $nuLink->expandedResult->selfLink->title = $propName;
759
                $nuLink->expandedResult->selfLink->url = $nuLink->url;
760
                $nuLink->expandedResult->title = $propName;
761
                $nuLink->expandedResult->id = rtrim($this->absoluteServiceUri, '/') . '/' . $nuLink->url;
762
            }
763
        }
764
    }
765
766
    /**
767
     * Gets the data service instance.
768
     *
769
     * @return IService
770
     */
771
    public function setService(IService $service)
772
    {
773
        $this->service = $service;
774
        $this->absoluteServiceUri = $service->getHost()->getAbsoluteServiceUri()->getUrlAsString();
775
        $this->absoluteServiceUriWithSlash = rtrim($this->absoluteServiceUri, '/') . '/';
776
    }
777
}
778