Completed
Push — master ( 425fe2...621ee6 )
by Alex
05:35
created

handleSegmentTargetsToResourceSet()   B

Complexity

Conditions 6
Paths 17

Size

Total Lines 26
Code Lines 21

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
cc 6
eloc 21
c 2
b 0
f 0
nc 17
nop 2
dl 0
loc 26
rs 8.9617
1
<?php
2
3
namespace POData\UriProcessor;
4
5
use POData\Common\HttpStatus;
6
use POData\Common\InvalidOperationException;
7
use POData\Common\Messages;
8
use POData\Common\ODataConstants;
9
use POData\Common\ODataException;
10
use POData\IService;
11
use POData\ObjectModel\CynicDeserialiser;
12
use POData\ObjectModel\ModelDeserialiser;
13
use POData\ObjectModel\ODataEntry;
14
use POData\ObjectModel\ODataURL;
15
use POData\OperationContext\HTTPRequestMethod;
16
use POData\Providers\Metadata\ResourcePropertyKind;
17
use POData\Providers\Metadata\ResourceSet;
18
use POData\Providers\Metadata\ResourceSetWrapper;
19
use POData\Providers\ProvidersWrapper;
20
use POData\Providers\Query\QueryResult;
21
use POData\Providers\Query\QueryType;
22
use POData\UriProcessor\Interfaces\IUriProcessor;
23
use POData\UriProcessor\QueryProcessor\QueryProcessor;
24
use POData\UriProcessor\ResourcePathProcessor\ResourcePathProcessor;
25
use POData\UriProcessor\ResourcePathProcessor\SegmentParser\KeyDescriptor;
26
use POData\UriProcessor\ResourcePathProcessor\SegmentParser\SegmentDescriptor;
27
use POData\UriProcessor\ResourcePathProcessor\SegmentParser\SegmentParser;
28
use POData\UriProcessor\ResourcePathProcessor\SegmentParser\TargetKind;
29
use POData\UriProcessor\ResourcePathProcessor\SegmentParser\TargetSource;
30
31
/**
32
 * Class UriProcessorNew.
33
 *
34
 * A type to process client's requested URI
35
 * The syntax of request URI is:
36
 *  Scheme Host Port ServiceRoot ResourcePath ? QueryOption
37
 * For more details refer:
38
 * http://www.odata.org/developers/protocols/uri-conventions#UriComponents
39
 */
40
class UriProcessorNew implements IUriProcessor
41
{
42
    /**
43
     * Description of the OData request that a client has submitted.
44
     *
45
     * @var RequestDescription
46
     */
47
    private $request;
48
49
    /**
50
     * Holds reference to the data service instance.
51
     *
52
     * @var IService
53
     */
54
    private $service;
55
56
    /**
57
     * Holds reference to the wrapper over IDSMP and IDSQP implementation.
58
     *
59
     * @var ProvidersWrapper
60
     */
61
    private $providers;
62
63
    /**
64
     * Holds reference to request expander.
65
     *
66
     * @var RequestExpander
67
     */
68
    private $expander;
69
70
    /**
71
     * @var ModelDeserialiser
72
     */
73
    private $cereal;
74
75
    /**
76
     * @var CynicDeserialiser
77
     */
78
    private $cynicDeserialiser;
79
80
    /**
81
     * Constructs a new instance of UriProcessor.
82
     *
83
     * @param IService $service Reference to the data service instance
84
     * @throws InvalidOperationException
85
     * @throws ODataException
86
     * @throws \Doctrine\Common\Annotations\AnnotationException
87
     * @throws \POData\Common\UrlFormatException
88
     * @throws \ReflectionException
89
     */
90
    private function __construct(IService $service)
91
    {
92
        $this->service = $service;
93
        $this->providers = $service->getProvidersWrapper();
94
        $this->request = ResourcePathProcessor::process($service);
95
        $this->expander = new RequestExpander(
96
            $this->getRequest(),
97
            $this->getService(),
98
            $this->getProviders()
99
        );
100
        $this->getRequest()->setUriProcessor($this);
101
        $this->cereal = new ModelDeserialiser();
102
        $this->cynicDeserialiser = new CynicDeserialiser(
103
            $service->getMetadataProvider(),
104
            $service->getProvidersWrapper()
105
        );
106
    }
107
108
    /**
109
     * Process the resource path and query options of client's request uri.
110
     *
111
     * @param IService $service Reference to the data service instance
112
     *
113
     * @return IUriProcessor
114
     * @throws InvalidOperationException
115
     * @throws ODataException
116
     * @throws \Doctrine\Common\Annotations\AnnotationException
117
     * @throws \POData\Common\NotImplementedException
118
     * @throws \ReflectionException
119
     * @throws \POData\Common\UrlFormatException
120
     */
121
    public static function process(IService $service)
122
    {
123
        $absRequestUri = $service->getHost()->getAbsoluteRequestUri();
124
        $absServiceUri = $service->getHost()->getAbsoluteServiceUri();
125
126
        if (!$absServiceUri->isBaseOf($absRequestUri)) {
127
            throw ODataException::createInternalServerError(
128
                Messages::uriProcessorRequestUriDoesNotHaveTheRightBaseUri(
129
                    $absRequestUri->getUrlAsString(),
130
                    $absServiceUri->getUrlAsString()
131
                )
132
            );
133
        }
134
135
        $processor = new self($service);
136
        //Parse the query string options of the request Uri.
137
        QueryProcessor::process($processor->request, $service);
138
        return $processor;
139
    }
140
141
    /**
142
     * Gets reference to the request submitted by client.
143
     *
144
     * @return RequestDescription
145
     */
146
    public function getRequest()
147
    {
148
        return $this->request;
149
    }
150
151
    /**
152
     * Gets reference to the request submitted by client.
153
     *
154
     * @return ProvidersWrapper
155
     */
156
    public function getProviders()
157
    {
158
        return $this->providers;
159
    }
160
161
    /**
162
     * Gets the data service instance.
163
     *
164
     * @return IService
165
     */
166
    public function getService()
167
    {
168
        return $this->service;
169
    }
170
171
    /**
172
     * Gets the request expander instance.
173
     *
174
     * @return RequestExpander
175
     */
176
    public function getExpander()
177
    {
178
        return $this->expander;
179
    }
180
181
    /**
182
     * @return ModelDeserialiser
183
     */
184
    public function getModelDeserialiser()
185
    {
186
        return $this->cereal;
187
    }
188
189
    /**
190
     * @return CynicDeserialiser
191
     */
192
    public function getCynicDeserialiser()
193
    {
194
        return $this->cynicDeserialiser;
195
    }
196
197
    /**
198
     * Execute the client submitted request against the data source.
199
     * @throws InvalidOperationException
200
     * @throws ODataException
201
     * @throws \POData\Common\UrlFormatException
202
     * @throws \ReflectionException
203
     */
204
    public function execute()
205
    {
206
        $service = $this->getService();
207
        if (!$service instanceof IService) {
0 ignored issues
show
introduced by
$service is always a sub-type of POData\IService.
Loading history...
208
            throw new InvalidOperationException('!($service instanceof IService)');
209
        }
210
        $context = $service->getOperationContext();
211
        $method = $context->incomingRequest()->getMethod();
212
213
        switch ($method) {
214
            case HTTPRequestMethod::GET():
215
                $this->executeGet();
216
                break;
217
            case HTTPRequestMethod::DELETE():
218
                $this->executeGet();
219
                $this->executeDelete();
220
                break;
221
            case HTTPRequestMethod::PUT():
222
                $this->executeGet();
223
                $this->executePut();
224
                break;
225
            case HTTPRequestMethod::POST():
226
                $this->executePost();
227
                break;
228
            default:
229
                throw ODataException::createNotImplementedError(Messages::onlyReadSupport($method));
230
        }
231
232
        // Apply $select and $expand options to result set, this function will be always applied
233
        // irrespective of return value of IDSQP2::canApplyQueryOptions which means library will
234
        // not delegate $expand/$select operation to IDSQP2 implementation
235
        $this->getExpander()->handleExpansion();
236
    }
237
238
    /**
239
     * Execute the client submitted request against the data source (GET).
240
     * @throws ODataException
241
     * @throws InvalidOperationException
242
     * @throws \ReflectionException
243
     */
244
    protected function executeGet()
245
    {
246
        $segments = $this->getRequest()->getSegments();
247
        $root = $this->getRequest()->getRootProjectionNode();
248
        $eagerLoad = (null === $root) ? [] : $root->getEagerLoadList();
249
250
        foreach ($segments as $segment) {
251
            $requestTargetKind = $segment->getTargetKind();
252
253
            switch ($requestTargetKind) {
254
                case TargetKind::SINGLETON():
255
                    $this->executeGetSingleton($segment);
256
                    break;
257
                case TargetKind::RESOURCE():
258
                    if (TargetSource::ENTITY_SET() == $segment->getTargetSource()) {
259
                        $this->handleSegmentTargetsToResourceSet($segment, $eagerLoad);
260
                    } else {
261
                        $this->executeGetResource($segment, $eagerLoad);
262
                    }
263
                    break;
264
                case TargetKind::MEDIA_RESOURCE():
265
                    $this->checkResourceExistsByIdentifier($segment);
266
                    $segment->setResult($segment->getPrevious()->getResult());
267
                    // a media resource means we're done - bail out of segment processing
268
                    break 2;
269
                case TargetKind::LINK():
270
                    $this->executeGetLink($segment);
271
                    break;
272
                case TargetKind::PRIMITIVE_VALUE():
273
                    $previous = $segment->getPrevious();
274
                    if (null !== $previous && TargetKind::RESOURCE() == $previous->getTargetKind()) {
275
                        $result = $previous->getResult();
276
                        if ($result instanceof QueryResult) {
277
                            $raw = null !== $result->count ? $result->count : count($result->results);
278
                            $segment->setResult($raw);
279
                        }
280
                    }
281
                    break;
282
                case TargetKind::PRIMITIVE():
283
                case TargetKind::COMPLEX_OBJECT():
284
                case TargetKind::BAG():
285
                    break;
286
                default:
287
                    throw new InvalidOperationException('Not implemented yet');
288
            }
289
290
            if (null === $segment->getNext()
291
                || ODataConstants::URI_COUNT_SEGMENT == $segment->getNext()->getIdentifier()
292
            ) {
293
                $this->applyQueryOptions($segment);
294
            }
295
        }
296
    }
297
298
    /**
299
     * Execute the client submitted request against the data source (DELETE).
300
     * @throws ODataException
301
     * @throws \POData\Common\UrlFormatException
302
     */
303
    protected function executeDelete()
304
    {
305
        $segment = $this->getFinalEffectiveSegment();
306
        $requestMethod = $this->getService()->getOperationContext()->incomingRequest()->getMethod();
307
        $resourceSet = $segment->getTargetResourceSetWrapper();
308
        $keyDescriptor = $segment->getKeyDescriptor();
309
310
        $this->checkUriValidForSuppliedVerb($resourceSet, $keyDescriptor, $requestMethod);
311
        assert($resourceSet instanceof ResourceSet);
312
        $this->getProviders()->deleteResource($resourceSet, $segment->getResult());
313
        $this->getService()->getHost()->setResponseStatusCode(HttpStatus::CODE_NOCONTENT);
314
    }
315
316
    /**
317
     * Execute the client submitted request against the data source (PUT).
318
     * @throws InvalidOperationException
319
     * @throws ODataException
320
     * @throws \ReflectionException
321
     * @throws \Exception
322
     */
323
    protected function executePut()
324
    {
325
        $segment = $this->getFinalEffectiveSegment();
326
        $requestMethod = $this->getService()->getOperationContext()->incomingRequest()->getMethod();
327
        $resourceSet = null !== $segment ? $segment->getTargetResourceSetWrapper() : null;
328
        $keyDescriptor = null !== $segment ? $segment->getKeyDescriptor() : null;
329
330
        $this->checkUriValidForSuppliedVerb($resourceSet, $keyDescriptor, $requestMethod);
331
        assert($resourceSet instanceof ResourceSet);
332
        assert($keyDescriptor instanceof KeyDescriptor);
333
334
        $payload = $this->getRequest()->getData();
335
        if (!$payload instanceof ODataEntry) {
336
            throw new InvalidOperationException(get_class($payload));
0 ignored issues
show
Bug introduced by
It seems like $payload can also be of type array; 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

336
            throw new InvalidOperationException(get_class(/** @scrutinizer ignore-type */ $payload));
Loading history...
337
        }
338
        if (empty($payload->id)) {
339
            throw new InvalidOperationException('Payload ID must not be empty for PUT request');
340
        }
341
        $data = $this->getModelDeserialiser()->bulkDeserialise($resourceSet->getResourceType(), $payload);
342
343
        if (empty($data)) {
344
            throw ODataException::createBadRequestError(Messages::noDataForThisVerb($requestMethod));
345
        }
346
347
        $queryResult = $this->getCynicDeserialiser()->processPayload($payload);
348
        $segment->setResult($queryResult);
349
    }
350
351
    /**
352
     * Execute the client submitted request against the data source (POST).
353
     * @throws InvalidOperationException
354
     * @throws ODataException
355
     * @throws \POData\Common\UrlFormatException
356
     * @throws \ReflectionException
357
     * @throws \Exception
358
     */
359
    protected function executePost()
360
    {
361
        $segments = $this->getRequest()->getSegments();
362
        $requestMethod = $this->getService()->getOperationContext()->incomingRequest()->getMethod();
363
364
        foreach ($segments as $segment) {
365
            $requestTargetKind = $segment->getTargetKind();
366
            if ($requestTargetKind == TargetKind::RESOURCE()) {
367
                $resourceSet = $segment->getTargetResourceSetWrapper();
368
                if (!$resourceSet) {
369
                    $url = $this->getService()->getHost()->getAbsoluteRequestUri()->getUrlAsString();
370
                    $msg = Messages::badRequestInvalidUriForThisVerb($url, $requestMethod);
371
                    throw ODataException::createBadRequestError($msg);
372
                }
373
374
                $payload = $this->getRequest()->getData();
375
                if ($payload instanceof ODataURL) {
376
                    $this->executeGet();
377
                    $masterModel = $this->getRequest()->getSegments()[0]->getResult();
378
                    $masterResourceSet = $this->getRequest()->getSegments()[0]->getTargetResourceSetWrapper();
379
                    $masterNavProperty = $this->getRequest()->getLastSegment()->getIdentifier();
380
                    $slaveModelUri = new \POData\Common\Url($payload->url);
381
                    $host = $this->getService()->getHost();
382
                    $absoluteServiceUri = $host->getAbsoluteServiceUri();
383
                    $requestUriSegments = array_slice(
384
                        $slaveModelUri->getSegments(),
385
                        $absoluteServiceUri->getSegmentCount()
386
                    );
387
                    $newSegments = SegmentParser::parseRequestUriSegments(
388
                        $requestUriSegments,
389
                        $this->getService()->getProvidersWrapper(),
390
                        true
391
                    );
392
                    $this->executeGetResource($newSegments[0]);
393
                    $slaveModel = $newSegments[0]->getResult();
394
                    $slaveResourceSet = $newSegments[0]->getTargetResourceSetWrapper();
395
                    $linkAdded = $this->getProviders()
396
                        ->hookSingleModel(
397
                            $masterResourceSet,
398
                            $masterModel,
399
                            $slaveResourceSet,
400
                            $slaveModel,
401
                            $masterNavProperty
402
                        );
403
                    if ($linkAdded) {
404
                        $this->getService()->getHost()->setResponseStatusCode(HttpStatus::CODE_NOCONTENT);
405
                    } else {
406
                        throw ODataException::createInternalServerError('AdapterIndicatedLinkNotAttached');
407
                    }
408
                    foreach ($segments as $segment) {
0 ignored issues
show
Comprehensibility Bug introduced by
$segment is overwriting a variable from outer foreach loop.
Loading history...
409
                        $segment->setResult(null);
410
                    }
411
                    return;
412
                }
413
                if (!$payload instanceof ODataEntry) {
414
                    throw new InvalidOperationException(get_class($payload));
0 ignored issues
show
Bug introduced by
It seems like $payload can also be of type array; 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

414
                    throw new InvalidOperationException(get_class(/** @scrutinizer ignore-type */ $payload));
Loading history...
415
                }
416
                if (!empty($payload->id)) {
417
                    throw new InvalidOperationException('Payload ID must be empty for POST request');
418
                }
419
420
                $data = $this->getModelDeserialiser()->bulkDeserialise($resourceSet->getResourceType(), $payload);
421
422
                if (empty($data)) {
423
                    throw ODataException::createBadRequestError(Messages::noDataForThisVerb($requestMethod));
424
                }
425
                $this->getService()->getHost()->setResponseStatusCode(HttpStatus::CODE_CREATED);
426
                $queryResult = $this->getCynicDeserialiser()->processPayload($payload);
427
                $keyID = $payload->id;
428
                if (!$keyID instanceof KeyDescriptor) {
429
                    throw new InvalidOperationException(get_class($keyID));
430
                }
431
432
                $locationUrl = $keyID->generateRelativeUri($resourceSet->getResourceSet());
433
                $absoluteServiceUri = $this->getService()->getHost()->getAbsoluteServiceUri()->getUrlAsString();
434
                $location = rtrim($absoluteServiceUri, '/') . '/' . $locationUrl;
435
                $this->getService()->getHost()->setResponseLocation($location);
436
                $segment->setResult($queryResult);
437
            }
438
        }
439
    }
440
441
    /**
442
     * @return null|SegmentDescriptor
443
     */
444
    protected function getFinalEffectiveSegment()
445
    {
446
        $segment = $this->getRequest()->getLastSegment();
447
        // if last segment is $count, back up one
448
        if (null !== $segment && ODataConstants::URI_COUNT_SEGMENT == $segment->getIdentifier()) {
449
            $segment = $segment->getPrevious();
450
            return $segment;
451
        }
452
        return $segment;
453
    }
454
455
    /**
456
     * @param $resourceSet
457
     * @param $keyDescriptor
458
     * @param $requestMethod
459
     * @throws ODataException
460
     * @throws \POData\Common\UrlFormatException
461
     */
462
    protected function checkUriValidForSuppliedVerb($resourceSet, $keyDescriptor, $requestMethod)
463
    {
464
        if (!$resourceSet || !$keyDescriptor) {
465
            $url = $this->getService()->getHost()->getAbsoluteRequestUri()->getUrlAsString();
466
            throw ODataException::createBadRequestError(
467
                Messages::badRequestInvalidUriForThisVerb($url, $requestMethod)
468
            );
469
        }
470
    }
471
472
    /**
473
     * @param SegmentDescriptor $segment
474
     */
475
    private function executeGetSingleton(SegmentDescriptor $segment)
476
    {
477
        $segmentId = $segment->getIdentifier();
478
        $singleton = $this->getService()->getProvidersWrapper()->resolveSingleton($segmentId);
479
        $segment->setResult($singleton->get());
480
    }
481
482
    /**
483
     * @param SegmentDescriptor $segment
484
     * @param array $eagerList
485
     * @throws InvalidOperationException
486
     * @throws ODataException
487
     * @throws \ReflectionException
488
     */
489
    private function executeGetResource(SegmentDescriptor $segment, array $eagerList = [])
490
    {
491
        foreach ($eagerList as $eager) {
492
            $nonEmpty = is_string($eager) && 0 < strlen($eager);
493
            if (!$nonEmpty) {
494
                throw new InvalidOperationException('Eager-load list elements must be non-empty strings');
495
            }
496
        }
497
        $isRelated = TargetSource::ENTITY_SET() == $segment->getTargetSource();
498
        if ($isRelated) {
499
            $queryResult = $this->executeGetResourceDirect($segment, $eagerList);
500
        } else {
501
            $queryResult = $this->executeGetResourceRelated($segment, $eagerList);
502
        }
503
        $segment->setResult($queryResult);
504
    }
505
506
    /**
507
     * @param SegmentDescriptor $segment
508
     */
509
    private function executeGetLink(SegmentDescriptor $segment)
510
    {
511
        $previous = $segment->getPrevious();
512
        assert(isset($previous));
513
        $segment->setResult($previous->getResult());
514
    }
515
516
    /**
517
     * @param SegmentDescriptor $segment
518
     * @param array $eagerList
519
     * @return null|object|QueryResult
520
     * @throws InvalidOperationException
521
     * @throws ODataException
522
     * @throws \ReflectionException
523
     */
524
    private function executeGetResourceDirect(SegmentDescriptor $segment, array $eagerList)
525
    {
526
        if ($segment->isSingleResult()) {
527
            $queryResult = $this->getProviders()->getResourceFromResourceSet(
528
                $segment->getTargetResourceSetWrapper(),
529
                $segment->getKeyDescriptor(),
530
                $eagerList
531
            );
532
        } else {
533
            $skip = $this->getRequest()->getSkipCount();
534
            $skip = (null === $skip) ? 0 : $skip;
535
            $skipToken = $this->getRequest()->getInternalSkipTokenInfo();
536
            $skipToken = (null != $skipToken) ? $skipToken->getSkipTokenInfo() : null;
537
            $queryResult = $this->getProviders()->getResourceSet(
538
                $this->getRequest()->queryType,
539
                $segment->getTargetResourceSetWrapper(),
540
                $this->getRequest()->getFilterInfo(),
541
                $this->getRequest()->getInternalOrderByInfo(),
542
                $this->getRequest()->getTopCount(),
543
                $skip,
544
                $skipToken,
545
                $eagerList
546
            );
547
        }
548
        return $queryResult;
549
    }
550
551
    /**
552
     * @param SegmentDescriptor $segment
553
     * @param $eagerList
554
     * @return null|object|QueryResult
555
     * @throws InvalidOperationException
556
     * @throws ODataException
557
     * @throws \ReflectionException
558
     */
559
    private function executeGetResourceRelated(SegmentDescriptor $segment, $eagerList)
0 ignored issues
show
Unused Code introduced by
The parameter $eagerList is not used and could be removed. ( Ignorable by Annotation )

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

559
    private function executeGetResourceRelated(SegmentDescriptor $segment, /** @scrutinizer ignore-unused */ $eagerList)

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

Loading history...
560
    {
561
        $projectedProperty = $segment->getProjectedProperty();
562
        $projectedPropertyKind = null !== $projectedProperty ? $projectedProperty->getKind() : 0;
563
        $queryResult = null;
564
        switch ($projectedPropertyKind) {
565
            case ResourcePropertyKind::RESOURCE_REFERENCE:
566
                $queryResult = $this->getProviders()->getRelatedResourceReference(
567
                    $segment->getPrevious()->getTargetResourceSetWrapper(),
568
                    $segment->getPrevious()->getResult(),
569
                    $segment->getTargetResourceSetWrapper(),
570
                    $projectedProperty
571
                );
572
                break;
573
            case ResourcePropertyKind::RESOURCESET_REFERENCE:
574
                if ($segment->isSingleResult()) {
575
                    $queryResult = $this->getProviders()->getResourceFromRelatedResourceSet(
576
                        $segment->getPrevious()->getTargetResourceSetWrapper(),
577
                        $segment->getPrevious()->getResult(),
578
                        $segment->getTargetResourceSetWrapper(),
579
                        $projectedProperty,
580
                        $segment->getKeyDescriptor()
581
                    );
582
                } else {
583
                    $skipToken = $this->getRequest()->getInternalSkipTokenInfo();
584
                    $skipToken = (null !== $skipToken) ? $skipToken->getSkipTokenInfo() : null;
585
                    $queryResult = $this->getProviders()->getRelatedResourceSet(
586
                        $this->getRequest()->queryType,
587
                        $segment->getPrevious()->getTargetResourceSetWrapper(),
588
                        $segment->getPrevious()->getResult(),
589
                        $segment->getTargetResourceSetWrapper(),
590
                        $projectedProperty,
591
                        $this->getRequest()->getFilterInfo(),
592
                        null, // $orderby
593
                        null, // $top
594
                        null, // $skip
595
                        $skipToken
596
                    );
597
                }
598
                break;
599
            default:
600
                $this->checkResourceExistsByIdentifier($segment);
601
                throw new InvalidOperationException('Invalid property kind type for resource retrieval');
602
        }
603
        return $queryResult;
604
    }
605
606
    /**
607
     * Query for a resource set pointed by the given segment descriptor and update the descriptor with the result.
608
     *
609
     * @param SegmentDescriptor $segment Describes the resource set to query
610
     * @param array|null $eagerLoad
611
     * @throws InvalidOperationException
612
     * @throws ODataException
613
     * @throws \ReflectionException
614
     */
615
    private function handleSegmentTargetsToResourceSet(SegmentDescriptor $segment, $eagerLoad)
616
    {
617
        if ($segment->isSingleResult()) {
618
            $entityInstance = $this->getProviders()->getResourceFromResourceSet(
619
                $segment->getTargetResourceSetWrapper(),
620
                $segment->getKeyDescriptor()
621
            );
622
623
            $segment->setResult($entityInstance);
624
        } else {
625
            $eagerLoad = (null !== $eagerLoad) ? $eagerLoad : [];
626
            $skip = (null == $this->getRequest()) ? 0 : $this->getRequest()->getSkipCount();
627
            $skip = (null === $skip) ? 0 : $skip;
628
            $skipToken = $this->getRequest()->getInternalSkipTokenInfo();
629
            $skipToken = (null != $skipToken) ? $skipToken->getSkipTokenInfo() : null;
630
            $queryResult = $this->getProviders()->getResourceSet(
631
                $this->getRequest()->queryType,
632
                $segment->getTargetResourceSetWrapper(),
633
                $this->getRequest()->getFilterInfo(),
634
                $this->getRequest()->getInternalOrderByInfo(),
635
                $this->getRequest()->getTopCount(),
636
                $skip,
637
                $skipToken,
638
                $eagerLoad
639
            );
640
            $segment->setResult($queryResult);
641
        }
642
    }
643
644
    /**
645
     * @param SegmentDescriptor $segment
646
     * @throws ODataException
647
     */
648
    private function checkResourceExistsByIdentifier(SegmentDescriptor $segment)
649
    {
650
        if (null === $segment->getPrevious()->getResult()) {
651
            throw ODataException::createResourceNotFoundError(
652
                $segment->getPrevious()->getIdentifier()
653
            );
654
        }
655
    }
656
657
    /**
658
     * Applies the query options to the resource(s) retrieved from the data source.
659
     *
660
     * @param SegmentDescriptor $segment The descriptor which holds resource(s) on which query options to be applied
661
     * @throws ODataException
662
     */
663
    private function applyQueryOptions(SegmentDescriptor $segment)
664
    {
665
        $result = $segment->getResult();
666
        if (!$result instanceof QueryResult) {
667
            //If the segment isn't a query result, then there's no paging or counting to be done
668
            return;
669
        }
670
        // Note $inlinecount=allpages means include the total count regardless of paging..so we set the counts first
671
        // regardless if POData does the paging or not.
672
        if ($this->getRequest()->queryType == QueryType::ENTITIES_WITH_COUNT()) {
673
            if ($this->getProviders()->handlesOrderedPaging()) {
674
                $this->getRequest()->setCountValue($result->count);
675
            } else {
676
                $this->getRequest()->setCountValue(count($result->results));
677
            }
678
        }
679
        //Have POData perform paging if necessary
680
        if (!$this->getProviders()->handlesOrderedPaging() && !empty($result->results)) {
681
            $result->results = $this->performPaging($result->results);
0 ignored issues
show
Bug introduced by
It seems like $result->results can also be of type object; however, parameter $result of POData\UriProcessor\UriP...sorNew::performPaging() does only seem to accept array, 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

681
            $result->results = $this->performPaging(/** @scrutinizer ignore-type */ $result->results);
Loading history...
682
        }
683
        //a bit surprising, but $skip and $top affects $count so update it here, not above
684
        //IE  data.svc/Collection/$count?$top=10 returns 10 even if Collection has 11+ entries
685
        if ($this->getRequest()->queryType == QueryType::COUNT()) {
686
            if ($this->getProviders()->handlesOrderedPaging()) {
687
                $this->getRequest()->setCountValue($result->count);
688
            } else {
689
                $this->getRequest()->setCountValue(count($result->results));
690
            }
691
        }
692
        $segment->setResult($result);
693
    }
694
695
    /**
696
     * If the provider does not perform the paging (ordering, top, skip) then this method does it.
697
     *
698
     * @param array $result
699
     *
700
     * @return array
701
     * @throws ODataException
702
     */
703
    private function performPaging(array $result)
704
    {
705
        //Apply (implicit and explicit) $orderby option
706
        $internalOrderByInfo = $this->getRequest()->getInternalOrderByInfo();
707
        if (null !== $internalOrderByInfo) {
708
            $orderByFunction = $internalOrderByInfo->getSorterFunction();
709
            usort($result, $orderByFunction);
710
        }
711
        //Apply $skiptoken option
712
        $internalSkipTokenInfo = $this->getRequest()->getInternalSkipTokenInfo();
713
        if (null !== $internalSkipTokenInfo) {
714
            $matchingIndex = $internalSkipTokenInfo->getIndexOfFirstEntryInTheNextPage($result);
715
            $result = array_slice($result, $matchingIndex);
716
        }
717
        //Apply $top and $skip option
718
        if (!empty($result)) {
719
            $top = $this->getRequest()->getTopCount();
720
            $skip = $this->getRequest()->getSkipCount();
721
            if (null === $skip) {
722
                $skip = 0;
723
            }
724
            $result = array_slice($result, $skip, $top);
725
        }
726
        return $result;
727
    }
728
}
729