1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
namespace POData\UriProcessor; |
4
|
|
|
|
5
|
|
|
use Illuminate\Support\Str; |
6
|
|
|
use POData\Providers\ProvidersWrapper; |
7
|
|
|
use POData\Providers\Metadata\ResourcePropertyKind; |
8
|
|
|
use POData\Providers\Metadata\ResourceTypeKind; |
9
|
|
|
use POData\Providers\Metadata\ResourceSetWrapper; |
10
|
|
|
use POData\Providers\Metadata\ResourceProperty; |
11
|
|
|
use POData\Providers\Query\QueryType; |
12
|
|
|
use POData\UriProcessor\QueryProcessor\QueryProcessor; |
13
|
|
|
use POData\UriProcessor\QueryProcessor\ExpandProjectionParser\ExpandedProjectionNode; |
14
|
|
|
use POData\UriProcessor\ResourcePathProcessor\ResourcePathProcessor; |
15
|
|
|
use POData\UriProcessor\ResourcePathProcessor\SegmentParser\SegmentDescriptor; |
16
|
|
|
use POData\UriProcessor\ResourcePathProcessor\SegmentParser\TargetKind; |
17
|
|
|
use POData\UriProcessor\ResourcePathProcessor\SegmentParser\TargetSource; |
18
|
|
|
use POData\IService; |
19
|
|
|
use POData\Common\Messages; |
20
|
|
|
use POData\Common\ODataException; |
21
|
|
|
use POData\Common\InvalidOperationException; |
22
|
|
|
use POData\Common\ODataConstants; |
23
|
|
|
use POData\Providers\Query\QueryResult; |
24
|
|
|
use POData\OperationContext\HTTPRequestMethod; |
25
|
|
|
|
26
|
|
|
/** |
27
|
|
|
* Class UriProcessor. |
28
|
|
|
* |
29
|
|
|
* A type to process client's requets URI |
30
|
|
|
* The syntax of request URI is: |
31
|
|
|
* Scheme Host Port ServiceRoot ResourcePath ? QueryOption |
32
|
|
|
* For more details refer: |
33
|
|
|
* http://www.odata.org/developers/protocols/uri-conventions#UriComponents |
34
|
|
|
*/ |
35
|
|
|
class UriProcessor |
36
|
|
|
{ |
37
|
|
|
/** |
38
|
|
|
* Description of the OData request that a client has submitted. |
39
|
|
|
* |
40
|
|
|
* @var RequestDescription |
41
|
|
|
*/ |
42
|
|
|
private $request; |
43
|
|
|
|
44
|
|
|
/** |
45
|
|
|
* Holds reference to the data service instance. |
46
|
|
|
* |
47
|
|
|
* @var IService |
48
|
|
|
*/ |
49
|
|
|
private $service; |
50
|
|
|
|
51
|
|
|
/** |
52
|
|
|
* Holds reference to the wrapper over IDSMP and IDSQP implementation. |
53
|
|
|
* |
54
|
|
|
* @var ProvidersWrapper |
55
|
|
|
*/ |
56
|
|
|
private $providers; |
57
|
|
|
|
58
|
|
|
/** |
59
|
|
|
* Holds reference to request expander |
60
|
|
|
* |
61
|
|
|
* @var RequestExpander |
62
|
|
|
*/ |
63
|
|
|
private $expander; |
64
|
|
|
|
65
|
|
|
/** |
66
|
|
|
* Constructs a new instance of UriProcessor. |
67
|
|
|
* |
68
|
|
|
* @param IService $service Reference to the data service instance |
69
|
|
|
*/ |
70
|
|
|
private function __construct(IService $service) |
71
|
|
|
{ |
72
|
|
|
$this->service = $service; |
73
|
|
|
$this->providers = $service->getProvidersWrapper(); |
74
|
|
|
} |
75
|
|
|
|
76
|
|
|
/** |
77
|
|
|
* Process the resource path and query options of client's request uri. |
78
|
|
|
* |
79
|
|
|
* @param IService $service Reference to the data service instance |
80
|
|
|
* |
81
|
|
|
* @return URIProcessor |
82
|
|
|
* |
83
|
|
|
* @throws ODataException |
84
|
|
|
*/ |
85
|
|
|
public static function process(IService $service) |
86
|
|
|
{ |
87
|
|
|
$absoluteRequestUri = $service->getHost()->getAbsoluteRequestUri(); |
88
|
|
|
$absoluteServiceUri = $service->getHost()->getAbsoluteServiceUri(); |
89
|
|
|
|
90
|
|
|
if (!$absoluteServiceUri->isBaseOf($absoluteRequestUri)) { |
91
|
|
|
throw ODataException::createInternalServerError( |
92
|
|
|
Messages::uriProcessorRequestUriDoesNotHaveTheRightBaseUri( |
93
|
|
|
$absoluteRequestUri->getUrlAsString(), |
94
|
|
|
$absoluteServiceUri->getUrlAsString() |
95
|
|
|
) |
96
|
|
|
); |
97
|
|
|
} |
98
|
|
|
|
99
|
|
|
$uriProcessor = new self($service); |
100
|
|
|
//Parse the resource path part of the request Uri. |
101
|
|
|
$uriProcessor->request = ResourcePathProcessor::process($service); |
102
|
|
|
$uriProcessor->expander = new RequestExpander( |
103
|
|
|
$uriProcessor->getRequest(), |
104
|
|
|
$uriProcessor->getService(), |
105
|
|
|
$uriProcessor->getProviders() |
106
|
|
|
); |
107
|
|
|
|
108
|
|
|
$uriProcessor->getRequest()->setUriProcessor($uriProcessor); |
109
|
|
|
|
110
|
|
|
//Parse the query string options of the request Uri. |
111
|
|
|
QueryProcessor::process($uriProcessor->request, $service); |
112
|
|
|
|
113
|
|
|
return $uriProcessor; |
114
|
|
|
} |
115
|
|
|
|
116
|
|
|
/** |
117
|
|
|
* Gets reference to the request submitted by client. |
118
|
|
|
* |
119
|
|
|
* @return RequestDescription |
120
|
|
|
*/ |
121
|
|
|
public function getRequest() |
122
|
|
|
{ |
123
|
|
|
return $this->request; |
124
|
|
|
} |
125
|
|
|
|
126
|
|
|
/** |
127
|
|
|
* Gets reference to the request submitted by client. |
128
|
|
|
* |
129
|
|
|
* @return ProvidersWrapper |
130
|
|
|
*/ |
131
|
|
|
public function getProviders() |
132
|
|
|
{ |
133
|
|
|
return $this->providers; |
134
|
|
|
} |
135
|
|
|
|
136
|
|
|
/** |
137
|
|
|
* Gets the data service instance |
138
|
|
|
* |
139
|
|
|
* @return IService |
140
|
|
|
*/ |
141
|
|
|
public function getService() |
142
|
|
|
{ |
143
|
|
|
return $this->service; |
144
|
|
|
} |
145
|
|
|
|
146
|
|
|
/** |
147
|
|
|
* Gets the request expander instance |
148
|
|
|
* |
149
|
|
|
* @return RequestExpander |
150
|
|
|
*/ |
151
|
|
|
public function getExpander() |
152
|
|
|
{ |
153
|
|
|
return $this->expander; |
154
|
|
|
} |
155
|
|
|
|
156
|
|
|
/** |
157
|
|
|
* Execute the client submitted request against the data source. |
158
|
|
|
*/ |
159
|
|
|
public function execute() |
160
|
|
|
{ |
161
|
|
|
$service = $this->getService(); |
162
|
|
|
$operationContext = !isset($service) ? null : $service->getOperationContext(); |
163
|
|
|
if (!$operationContext) { |
164
|
|
|
$this->executeBase(); |
165
|
|
|
|
166
|
|
|
return; |
167
|
|
|
} |
168
|
|
|
|
169
|
|
|
$requestMethod = $operationContext->incomingRequest()->getMethod(); |
170
|
|
|
if ($requestMethod == HTTPRequestMethod::GET()) { |
171
|
|
|
$this->executeGet(); |
172
|
|
|
} elseif ($requestMethod == HTTPRequestMethod::POST()) { |
173
|
|
|
$this->executePost(); |
174
|
|
|
} elseif ($requestMethod == HTTPRequestMethod::PUT()) { |
175
|
|
|
$this->executePut(); |
176
|
|
|
} elseif ($requestMethod == HTTPRequestMethod::DELETE()) { |
177
|
|
|
$this->executeDelete(); |
178
|
|
|
//TODO: we probably need these verbs eventually. |
179
|
|
|
/*} elseif ($requestMethod == HTTPRequestMethod::PATCH()) { |
|
|
|
|
180
|
|
|
$this->executePatch(); |
181
|
|
|
} elseif ($requestMethod == HTTPRequestMethod::MERGE()) { |
182
|
|
|
$this->executeMerge();*/ |
183
|
|
|
} else { |
184
|
|
|
throw ODataException::createNotImplementedError(Messages::onlyReadSupport($requestMethod)); |
185
|
|
|
} |
186
|
|
|
} |
187
|
|
|
|
188
|
|
|
/** |
189
|
|
|
* Execute the client submitted request against the data source (GET). |
190
|
|
|
*/ |
191
|
|
|
protected function executeGet() |
192
|
|
|
{ |
193
|
|
|
return $this->executeBase(); |
194
|
|
|
} |
195
|
|
|
|
196
|
|
|
/** |
197
|
|
|
* Execute the client submitted request against the data source (POST). |
198
|
|
|
*/ |
199
|
|
|
protected function executePost() |
200
|
|
|
{ |
201
|
|
|
$segments = $this->getRequest()->getSegments(); |
202
|
|
|
|
203
|
|
|
foreach ($segments as $segment) { |
204
|
|
|
$requestTargetKind = $segment->getTargetKind(); |
205
|
|
|
if ($requestTargetKind == TargetKind::RESOURCE()) { |
206
|
|
|
$requestMethod = $this->getService()->getOperationContext()->incomingRequest()->getMethod(); |
207
|
|
|
$resourceSet = $segment->getTargetResourceSetWrapper(); |
208
|
|
|
$keyDescriptor = $segment->getKeyDescriptor(); |
209
|
|
|
if (!$resourceSet) { |
210
|
|
|
$url = $this->getService()->getHost()->getAbsoluteRequestUri()->getUrlAsString(); |
211
|
|
|
throw ODataException::createBadRequestError( |
212
|
|
|
Messages::badRequestInvalidUriForThisVerb($url, $requestMethod) |
213
|
|
|
); |
214
|
|
|
} |
215
|
|
|
$data = $this->getRequest()->getData(); |
216
|
|
|
if (!$data) { |
|
|
|
|
217
|
|
|
throw ODataException::createBadRequestError(Messages::noDataForThisVerb($requestMethod)); |
218
|
|
|
} |
219
|
|
|
$queryResult = $this->getProviders()->createResourceforResourceSet($resourceSet, $keyDescriptor, $data); |
|
|
|
|
220
|
|
|
$segment->setResult($queryResult); |
221
|
|
|
} |
222
|
|
|
} |
223
|
|
|
//return $this->executeBase(); |
|
|
|
|
224
|
|
|
} |
225
|
|
|
|
226
|
|
|
/** |
227
|
|
|
* Execute the client submitted request against the data source (PUT). |
228
|
|
|
*/ |
229
|
|
|
protected function executePut() |
230
|
|
|
{ |
231
|
|
|
return $this->executeBase(function($uriProcessor, $segment) { |
232
|
|
|
$requestMethod = $uriProcessor->getService()->getOperationContext()->incomingRequest()->getMethod(); |
233
|
|
|
$resourceSet = $segment->getTargetResourceSetWrapper(); |
234
|
|
|
$keyDescriptor = $segment->getKeyDescriptor(); |
235
|
|
|
if (!$resourceSet || !$keyDescriptor) { |
236
|
|
|
$url = $uriProcessor->getService()->getHost()->getAbsoluteRequestUri()->getUrlAsString(); |
237
|
|
|
throw ODataException::createBadRequestError(Messages::badRequestInvalidUriForThisVerb($url, $requestMethod)); |
238
|
|
|
} |
239
|
|
|
|
240
|
|
|
$data = $uriProcessor->getRequest()->getData(); |
241
|
|
|
if (!$data) { |
242
|
|
|
throw ODataException::createBadRequestError(Messages::noDataForThisVerb($requestMethod)); |
243
|
|
|
} |
244
|
|
|
|
245
|
|
|
$queryResult = $uriProcessor->getProviders()->updateResource( |
246
|
|
|
$resourceSet, |
247
|
|
|
$segment->getResult(), |
248
|
|
|
$keyDescriptor, |
249
|
|
|
$data, |
250
|
|
|
false |
251
|
|
|
); |
252
|
|
|
$segment->setResult($queryResult); |
253
|
|
|
return $queryResult; |
254
|
|
|
}); |
255
|
|
|
} |
256
|
|
|
|
257
|
|
|
/** |
258
|
|
|
* Execute the client submitted request against the data source (DELETE). |
259
|
|
|
*/ |
260
|
|
|
protected function executeDelete() |
261
|
|
|
{ |
262
|
|
|
return $this->executeBase(function($uriProcessor, $segment) { |
263
|
|
|
$requestMethod = $uriProcessor->getService()->getOperationContext()->incomingRequest()->getMethod(); |
264
|
|
|
$resourceSet = $segment->getTargetResourceSetWrapper(); |
265
|
|
|
$keyDescriptor = $segment->getKeyDescriptor(); |
266
|
|
|
if (!$resourceSet || !$keyDescriptor) { |
267
|
|
|
$url = $uriProcessor->getService()->getHost()->getAbsoluteRequestUri()->getUrlAsString(); |
268
|
|
|
throw ODataException::createBadRequestError( |
269
|
|
|
Messages::badRequestInvalidUriForThisVerb($url, $requestMethod) |
270
|
|
|
); |
271
|
|
|
} |
272
|
|
|
return $uriProcessor->getProviders()->deleteResource($resourceSet, $segment->getResult()); |
273
|
|
|
}); |
274
|
|
|
} |
275
|
|
|
|
276
|
|
|
/** |
277
|
|
|
* Execute the client submitted request against the data source. |
278
|
|
|
* |
279
|
|
|
* @param callable $callback Function, what must be called |
280
|
|
|
*/ |
281
|
|
|
protected function executeBase($callback = null) |
282
|
|
|
{ |
283
|
|
|
$segments = $this->getRequest()->getSegments(); |
284
|
|
|
|
285
|
|
|
foreach ($segments as $segment) { |
286
|
|
|
$requestTargetKind = $segment->getTargetKind(); |
287
|
|
|
|
288
|
|
|
if ($segment->getTargetSource() == TargetSource::ENTITY_SET) { |
289
|
|
|
$this->handleSegmentTargetsToResourceSet($segment); |
290
|
|
|
} elseif ($requestTargetKind == TargetKind::RESOURCE()) { |
291
|
|
|
if (is_null($segment->getPrevious()->getResult())) { |
292
|
|
|
throw ODataException::createResourceNotFoundError( |
293
|
|
|
$segment->getPrevious()->getIdentifier() |
294
|
|
|
); |
295
|
|
|
} |
296
|
|
|
$this->_handleSegmentTargetsToRelatedResource($segment); |
297
|
|
|
} elseif ($requestTargetKind == TargetKind::LINK()) { |
298
|
|
|
$segment->setResult($segment->getPrevious()->getResult()); |
299
|
|
|
} elseif ($segment->getIdentifier() == ODataConstants::URI_COUNT_SEGMENT) { |
300
|
|
|
// we are done, $count will the last segment and |
301
|
|
|
// taken care by _applyQueryOptions method |
302
|
|
|
$segment->setResult($this->getRequest()->getCountValue()); |
303
|
|
|
break; |
304
|
|
|
} else { |
305
|
|
|
if ($requestTargetKind == TargetKind::MEDIA_RESOURCE()) { |
306
|
|
|
if (is_null($segment->getPrevious()->getResult())) { |
307
|
|
|
throw ODataException::createResourceNotFoundError( |
308
|
|
|
$segment->getPrevious()->getIdentifier() |
309
|
|
|
); |
310
|
|
|
} |
311
|
|
|
// For MLE and Named Stream the result of last segment |
312
|
|
|
// should be that of previous segment, this is required |
313
|
|
|
// while retrieving content type or stream from IDSSP |
314
|
|
|
$segment->setResult($segment->getPrevious()->getResult()); |
315
|
|
|
// we are done, as named stream property or $value on |
316
|
|
|
// media resource will be the last segment |
317
|
|
|
break; |
318
|
|
|
} |
319
|
|
|
|
320
|
|
|
$value = $segment->getPrevious()->getResult(); |
321
|
|
|
while (!is_null($segment)) { |
322
|
|
|
//TODO: what exactly is this doing here? Once a null's found it seems everything will be null |
323
|
|
|
if (!is_null($value)) { |
324
|
|
|
$value = null; |
325
|
|
|
} else { |
326
|
|
|
// This is theoretically impossible to reach, but should that be changed, this will need to call |
327
|
|
|
// ResourceType::getPropertyValue... somehow |
328
|
|
|
try { |
329
|
|
|
//see #88 |
330
|
|
|
$property = new \ReflectionProperty($value, $segment->getIdentifier()); |
331
|
|
|
$value = $property->getValue($value); |
332
|
|
|
} catch (\ReflectionException $reflectionException) { |
|
|
|
|
333
|
|
|
|
334
|
|
|
} |
335
|
|
|
} |
336
|
|
|
|
337
|
|
|
$segment->setResult($value); |
338
|
|
|
$segment = $segment->getNext(); |
339
|
|
|
if (!is_null($segment) && ODataConstants::URI_VALUE_SEGMENT == $segment->getIdentifier()) { |
340
|
|
|
$segment->setResult($value); |
341
|
|
|
$segment = $segment->getNext(); |
342
|
|
|
} |
343
|
|
|
} |
344
|
|
|
|
345
|
|
|
break; |
346
|
|
|
} |
347
|
|
|
|
348
|
|
|
if (is_null($segment->getNext()) |
349
|
|
|
|| ODataConstants::URI_COUNT_SEGMENT == $segment->getNext()->getIdentifier() |
350
|
|
|
) { |
351
|
|
|
$this->applyQueryOptions($segment, $callback); |
352
|
|
|
} |
353
|
|
|
} |
354
|
|
|
|
355
|
|
|
// Apply $select and $expand options to result set, this function will be always applied |
356
|
|
|
// irrespective of return value of IDSQP2::canApplyQueryOptions which means library will |
357
|
|
|
// not delegate $expand/$select operation to IDSQP2 implementation |
358
|
|
|
$this->handleExpansion(); |
359
|
|
|
} |
360
|
|
|
|
361
|
|
|
/** |
362
|
|
|
* Query for a resource set pointed by the given segment descriptor and update the descriptor with the result. |
363
|
|
|
* |
364
|
|
|
* @param SegmentDescriptor $segment Describes the resource set to query |
365
|
|
|
*/ |
366
|
|
|
private function handleSegmentTargetsToResourceSet(SegmentDescriptor $segment) |
367
|
|
|
{ |
368
|
|
|
if ($segment->isSingleResult()) { |
369
|
|
|
$entityInstance = $this->getProviders()->getResourceFromResourceSet( |
370
|
|
|
$segment->getTargetResourceSetWrapper(), |
371
|
|
|
$segment->getKeyDescriptor() |
372
|
|
|
); |
373
|
|
|
|
374
|
|
|
$segment->setResult($entityInstance); |
375
|
|
|
} else { |
376
|
|
|
$skip = (null == $this->getRequest()) ? 0 : $this->getRequest()->getSkipCount(); |
377
|
|
|
$skip = (null == $skip) ? 0 :$skip; |
|
|
|
|
378
|
|
|
$queryResult = $this->getProviders()->getResourceSet( |
379
|
|
|
$this->getRequest()->queryType, |
380
|
|
|
$segment->getTargetResourceSetWrapper(), |
381
|
|
|
$this->getRequest()->getFilterInfo(), |
382
|
|
|
$this->getRequest()->getInternalOrderByInfo(), |
383
|
|
|
$this->getRequest()->getTopCount(), |
384
|
|
|
$skip, |
385
|
|
|
null |
386
|
|
|
); |
387
|
|
|
$segment->setResult($queryResult); |
388
|
|
|
} |
389
|
|
|
} |
390
|
|
|
|
391
|
|
|
/** |
392
|
|
|
* Query for a related resource set or resource set reference pointed by the |
393
|
|
|
* given segment descriptor and update the descriptor with the result. |
394
|
|
|
* |
395
|
|
|
* @param SegmentDescriptor &$segment Describes the related resource |
396
|
|
|
* to query |
397
|
|
|
*/ |
398
|
|
|
private function _handleSegmentTargetsToRelatedResource(SegmentDescriptor $segment) |
399
|
|
|
{ |
400
|
|
|
$projectedProperty = $segment->getProjectedProperty(); |
401
|
|
|
$projectedPropertyKind = $projectedProperty->getKind(); |
402
|
|
|
|
403
|
|
|
if ($projectedPropertyKind == ResourcePropertyKind::RESOURCESET_REFERENCE) { |
404
|
|
|
if ($segment->isSingleResult()) { |
405
|
|
|
$entityInstance = $this->getProviders()->getResourceFromRelatedResourceSet( |
406
|
|
|
$segment->getPrevious()->getTargetResourceSetWrapper(), |
407
|
|
|
$segment->getPrevious()->getResult(), |
408
|
|
|
$segment->getTargetResourceSetWrapper(), |
409
|
|
|
$projectedProperty, |
410
|
|
|
$segment->getKeyDescriptor() |
411
|
|
|
); |
412
|
|
|
|
413
|
|
|
$segment->setResult($entityInstance); |
414
|
|
|
} else { |
415
|
|
|
$queryResult = $this->getProviders()->getRelatedResourceSet( |
416
|
|
|
$this->getRequest()->queryType, |
417
|
|
|
$segment->getPrevious()->getTargetResourceSetWrapper(), |
418
|
|
|
$segment->getPrevious()->getResult(), |
419
|
|
|
$segment->getTargetResourceSetWrapper(), |
420
|
|
|
$segment->getProjectedProperty(), |
421
|
|
|
$this->getRequest()->getFilterInfo(), |
|
|
|
|
422
|
|
|
//TODO: why are these null? see #98 |
423
|
|
|
null, // $orderby |
424
|
|
|
null, // $top |
425
|
|
|
null // $skip |
426
|
|
|
); |
427
|
|
|
|
428
|
|
|
$segment->setResult($queryResult); |
429
|
|
|
} |
430
|
|
|
} elseif ($projectedPropertyKind == ResourcePropertyKind::RESOURCE_REFERENCE) { |
431
|
|
|
|
432
|
|
|
$entityInstance = $this->getProviders()->getRelatedResourceReference( |
433
|
|
|
$segment->getPrevious()->getTargetResourceSetWrapper(), |
434
|
|
|
$segment->getPrevious()->getResult(), |
435
|
|
|
$segment->getTargetResourceSetWrapper(), |
436
|
|
|
$segment->getProjectedProperty() |
437
|
|
|
); |
438
|
|
|
|
439
|
|
|
$segment->setResult($entityInstance); |
440
|
|
|
} else { |
|
|
|
|
441
|
|
|
//Unexpected state |
442
|
|
|
} |
443
|
|
|
} |
444
|
|
|
|
445
|
|
|
/** |
446
|
|
|
* Applies the query options to the resource(s) retrieved from the data source. |
447
|
|
|
* |
448
|
|
|
* @param SegmentDescriptor $segment The descriptor which holds resource(s) on which query options to be applied |
449
|
|
|
* @param callable $callback Function, what must be called |
450
|
|
|
*/ |
451
|
|
|
private function applyQueryOptions(SegmentDescriptor $segment, $callback = null) |
452
|
|
|
{ |
453
|
|
|
// For non-GET methods |
454
|
|
|
if ($callback) { |
455
|
|
|
$callback($this, $segment); |
456
|
|
|
|
457
|
|
|
return; |
458
|
|
|
} |
459
|
|
|
|
460
|
|
|
//TODO: I'm not really happy with this..i think i'd rather keep the result the QueryResult |
461
|
|
|
//not even bother with the setCountValue stuff (shouldn't counts be on segments?) |
462
|
|
|
//and just work with the QueryResult in the object model serializer |
463
|
|
|
$result = $segment->getResult(); |
464
|
|
|
|
465
|
|
|
if (!$result instanceof QueryResult) { |
466
|
|
|
//If the segment isn't a query result, then there's no paging or counting to be done |
467
|
|
|
return; |
468
|
|
|
} |
469
|
|
|
|
470
|
|
|
// Note $inlinecount=allpages means include the total count regardless of paging..so we set the counts first |
471
|
|
|
// regardless if POData does the paging or not. |
472
|
|
View Code Duplication |
if ($this->getRequest()->queryType == QueryType::ENTITIES_WITH_COUNT()) { |
|
|
|
|
473
|
|
|
if ($this->getProviders()->handlesOrderedPaging()) { |
474
|
|
|
$this->getRequest()->setCountValue($result->count); |
475
|
|
|
} else { |
476
|
|
|
$this->getRequest()->setCountValue(count($result->results)); |
477
|
|
|
} |
478
|
|
|
} |
479
|
|
|
|
480
|
|
|
//Have POData perform paging if necessary |
481
|
|
|
if (!$this->getProviders()->handlesOrderedPaging() && !empty($result->results)) { |
482
|
|
|
$result->results = $this->performPaging($result->results); |
483
|
|
|
} |
484
|
|
|
|
485
|
|
|
//a bit surprising, but $skip and $top affects $count so update it here, not above |
486
|
|
|
//IE data.svc/Collection/$count?$top=10 returns 10 even if Collection has 11+ entries |
487
|
|
View Code Duplication |
if ($this->getRequest()->queryType == QueryType::COUNT()) { |
|
|
|
|
488
|
|
|
if ($this->getProviders()->handlesOrderedPaging()) { |
489
|
|
|
$this->getRequest()->setCountValue($result->count); |
490
|
|
|
} else { |
491
|
|
|
$this->getRequest()->setCountValue(count($result->results)); |
492
|
|
|
} |
493
|
|
|
} |
494
|
|
|
|
495
|
|
|
$segment->setResult($result->results); |
496
|
|
|
} |
497
|
|
|
|
498
|
|
|
/** |
499
|
|
|
* If the provider does not perform the paging (ordering, top, skip) then this method does it. |
500
|
|
|
* |
501
|
|
|
* @param array $result |
502
|
|
|
* |
503
|
|
|
* @return array |
504
|
|
|
*/ |
505
|
|
|
private function performPaging(array $result) |
506
|
|
|
{ |
507
|
|
|
//Apply (implicit and explicit) $orderby option |
508
|
|
|
$internalOrderByInfo = $this->getRequest()->getInternalOrderByInfo(); |
509
|
|
|
if (!is_null($internalOrderByInfo)) { |
510
|
|
|
$orderByFunction = $internalOrderByInfo->getSorterFunction()->getReference(); |
511
|
|
|
usort($result, $orderByFunction); |
512
|
|
|
} |
513
|
|
|
|
514
|
|
|
//Apply $skiptoken option |
515
|
|
|
$internalSkipTokenInfo = $this->getRequest()->getInternalSkipTokenInfo(); |
516
|
|
|
if (!is_null($internalSkipTokenInfo)) { |
517
|
|
|
$matchingIndex = $internalSkipTokenInfo->getIndexOfFirstEntryInTheNextPage($result); |
518
|
|
|
$result = array_slice($result, $matchingIndex); |
519
|
|
|
} |
520
|
|
|
|
521
|
|
|
//Apply $top and $skip option |
522
|
|
|
if (!empty($result)) { |
523
|
|
|
$top = $this->getRequest()->getTopCount(); |
524
|
|
|
$skip = $this->getRequest()->getSkipCount(); |
525
|
|
|
if (is_null($skip)) { |
526
|
|
|
$skip = 0; |
527
|
|
|
} |
528
|
|
|
|
529
|
|
|
$result = array_slice($result, $skip, $top); |
530
|
|
|
} |
531
|
|
|
|
532
|
|
|
return $result; |
533
|
|
|
} |
534
|
|
|
|
535
|
|
|
/** |
536
|
|
|
* Perform expansion. |
537
|
|
|
*/ |
538
|
|
|
private function handleExpansion() |
539
|
|
|
{ |
540
|
|
|
$this->getExpander()->handleExpansion(); |
541
|
|
|
} |
542
|
|
|
} |
543
|
|
|
|
Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.
The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.
This check looks for comments that seem to be mostly valid code and reports them.