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\ODataPropertyContent; |
16
|
|
|
use POData\ObjectModel\ODataURL; |
17
|
|
|
use POData\ObjectModel\ODataURLCollection; |
18
|
|
|
use POData\Providers\Metadata\ResourceEntityType; |
19
|
|
|
use POData\Providers\Metadata\ResourceProperty; |
20
|
|
|
use POData\Providers\Metadata\ResourcePropertyKind; |
21
|
|
|
use POData\Providers\Metadata\ResourceSet; |
22
|
|
|
use POData\Providers\Metadata\ResourceSetWrapper; |
23
|
|
|
use POData\Providers\Metadata\ResourceType; |
24
|
|
|
use POData\Providers\Metadata\Type\IType; |
25
|
|
|
use POData\Providers\Query\QueryType; |
26
|
|
|
use POData\UriProcessor\QueryProcessor\ExpandProjectionParser\ExpandedProjectionNode; |
27
|
|
|
use POData\UriProcessor\QueryProcessor\ExpandProjectionParser\ProjectionNode; |
28
|
|
|
use POData\UriProcessor\RequestDescription; |
29
|
|
|
use POData\UriProcessor\SegmentStack; |
30
|
|
|
|
31
|
|
|
class IronicSerialiser implements IObjectSerialiser |
32
|
|
|
{ |
33
|
|
|
/** |
34
|
|
|
* The service implementation. |
35
|
|
|
* |
36
|
|
|
* @var IService |
37
|
|
|
*/ |
38
|
|
|
protected $service; |
39
|
|
|
|
40
|
|
|
/** |
41
|
|
|
* Request description instance describes OData request the |
42
|
|
|
* the client has submitted and result of the request. |
43
|
|
|
* |
44
|
|
|
* @var RequestDescription |
45
|
|
|
*/ |
46
|
|
|
protected $request; |
47
|
|
|
|
48
|
|
|
/** |
49
|
|
|
* Collection of complex type instances used for cycle detection. |
50
|
|
|
* |
51
|
|
|
* @var array |
52
|
|
|
*/ |
53
|
|
|
protected $complexTypeInstanceCollection; |
54
|
|
|
|
55
|
|
|
/** |
56
|
|
|
* Absolute service Uri. |
57
|
|
|
* |
58
|
|
|
* @var string |
59
|
|
|
*/ |
60
|
|
|
protected $absoluteServiceUri; |
61
|
|
|
|
62
|
|
|
/** |
63
|
|
|
* Absolute service Uri with slash. |
64
|
|
|
* |
65
|
|
|
* @var string |
66
|
|
|
*/ |
67
|
|
|
protected $absoluteServiceUriWithSlash; |
68
|
|
|
|
69
|
|
|
/** |
70
|
|
|
* Holds reference to segment stack being processed. |
71
|
|
|
* |
72
|
|
|
* @var SegmentStack |
73
|
|
|
*/ |
74
|
|
|
protected $stack; |
75
|
|
|
|
76
|
|
|
/** |
77
|
|
|
* Lightweight stack tracking for recursive descent fill |
78
|
|
|
*/ |
79
|
|
|
private $lightStack = []; |
80
|
|
|
|
81
|
|
|
/** |
82
|
|
|
* @param IService $service Reference to the data service instance |
83
|
|
|
* @param RequestDescription $request Type instance describing the client submitted request |
84
|
|
|
*/ |
85
|
|
|
public function __construct(IService $service, RequestDescription $request = null) |
86
|
|
|
{ |
87
|
|
|
$this->service = $service; |
88
|
|
|
$this->request = $request; |
89
|
|
|
$this->absoluteServiceUri = $service->getHost()->getAbsoluteServiceUri()->getUrlAsString(); |
90
|
|
|
$this->absoluteServiceUriWithSlash = rtrim($this->absoluteServiceUri, '/') . '/'; |
91
|
|
|
$this->stack = new SegmentStack($request); |
92
|
|
|
$this->complexTypeInstanceCollection = []; |
93
|
|
|
} |
94
|
|
|
|
95
|
|
|
/** |
96
|
|
|
* Write a top level entry resource. |
97
|
|
|
* |
98
|
|
|
* @param mixed $entryObject Reference to the entry object to be written |
99
|
|
|
* |
100
|
|
|
* @return ODataEntry |
101
|
|
|
*/ |
102
|
|
|
public function writeTopLevelElement($entryObject) |
103
|
|
|
{ |
104
|
|
|
if (!isset($entryObject)) { |
105
|
|
|
array_pop($this->lightStack); |
106
|
|
|
return null; |
107
|
|
|
} |
108
|
|
|
|
109
|
|
View Code Duplication |
if (0 == count($this->lightStack)) { |
|
|
|
|
110
|
|
|
$typeName = $this->getRequest()->getTargetResourceType()->getName(); |
111
|
|
|
array_push($this->lightStack, [$typeName, $typeName]); |
112
|
|
|
} |
113
|
|
|
|
114
|
|
|
$stackCount = count($this->lightStack); |
115
|
|
|
$topOfStack = $this->lightStack[$stackCount-1]; |
116
|
|
|
$resourceType = $this->getService()->getProvidersWrapper()->resolveResourceType($topOfStack[0]); |
117
|
|
|
$rawProp = $resourceType->getAllProperties(); |
118
|
|
|
$relProp = []; |
119
|
|
|
foreach ($rawProp as $prop) { |
120
|
|
|
if ($prop->getResourceType() instanceof ResourceEntityType) { |
121
|
|
|
$relProp[] = $prop; |
122
|
|
|
} |
123
|
|
|
} |
124
|
|
|
|
125
|
|
|
$resourceSet = $resourceType->getCustomState(); |
126
|
|
|
assert($resourceSet instanceof ResourceSet); |
127
|
|
|
$title = $resourceType->getName(); |
128
|
|
|
$type = $resourceType->getFullName(); |
129
|
|
|
|
130
|
|
|
$relativeUri = $this->getEntryInstanceKey( |
131
|
|
|
$entryObject, |
132
|
|
|
$resourceType, |
133
|
|
|
$resourceSet->getName() |
134
|
|
|
); |
135
|
|
|
$absoluteUri = rtrim($this->absoluteServiceUri, '/') . '/' . $relativeUri; |
136
|
|
|
|
137
|
|
|
list($mediaLink, $mediaLinks) = $this->writeMediaData($entryObject, $type, $relativeUri, $resourceType); |
138
|
|
|
|
139
|
|
|
$propertyContent = new ODataPropertyContent(); |
140
|
|
|
|
141
|
|
|
$links = []; |
142
|
|
|
foreach ($relProp as $prop) { |
143
|
|
|
$nuLink = new ODataLink(); |
144
|
|
|
$propKind = $prop->getKind(); |
145
|
|
|
|
146
|
|
|
assert( |
147
|
|
|
ResourcePropertyKind::RESOURCESET_REFERENCE == $propKind |
148
|
|
|
|| ResourcePropertyKind::RESOURCE_REFERENCE == $propKind, |
149
|
|
|
'$propKind != ResourcePropertyKind::RESOURCESET_REFERENCE &&' |
150
|
|
|
.' $propKind != ResourcePropertyKind::RESOURCE_REFERENCE' |
151
|
|
|
); |
152
|
|
|
$propTail = ResourcePropertyKind::RESOURCE_REFERENCE == $propKind ? 'entry' : 'feed'; |
153
|
|
|
$propType = 'application/atom+xml;type='.$propTail; |
154
|
|
|
$propName = $prop->getName(); |
155
|
|
|
$nuLink->title = $propName; |
156
|
|
|
$nuLink->name = ODataConstants::ODATA_RELATED_NAMESPACE . $propName; |
157
|
|
|
$nuLink->url = $relativeUri . '/' . $propName; |
158
|
|
|
$nuLink->type = $propType; |
159
|
|
|
|
160
|
|
|
$navProp = new ODataNavigationPropertyInfo($prop, $this->shouldExpandSegment($propName)); |
161
|
|
|
if ($navProp->expanded) { |
162
|
|
|
$nextName = $prop->getResourceType()->getName(); |
163
|
|
|
$nuLink->isExpanded = true; |
164
|
|
|
$isCollection = ResourcePropertyKind::RESOURCESET_REFERENCE == $propKind; |
165
|
|
|
$nuLink->isCollection = $isCollection; |
166
|
|
|
$value = $entryObject->$propName; |
167
|
|
|
array_push($this->lightStack, [$nextName, $propName]); |
168
|
|
|
if (!$isCollection) { |
169
|
|
|
$expandedResult = $this->writeTopLevelElement($value); |
170
|
|
|
} else { |
171
|
|
|
$expandedResult = $this->writeTopLevelElements($value); |
172
|
|
|
} |
173
|
|
|
$nuLink->expandedResult = $expandedResult; |
174
|
|
|
if (!isset($nuLink->expandedResult)) { |
175
|
|
|
$nuLink->isCollection = null; |
176
|
|
|
$nuLink->isExpanded = null; |
177
|
|
|
} else { |
178
|
|
|
if (isset($nuLink->expandedResult->selfLink)) { |
179
|
|
|
$nuLink->expandedResult->selfLink->title = $propName; |
180
|
|
|
$nuLink->expandedResult->selfLink->url = $nuLink->url; |
181
|
|
|
$nuLink->expandedResult->title = $propName; |
182
|
|
|
$nuLink->expandedResult->id = rtrim($this->absoluteServiceUri, '/') . '/' . $nuLink->url; |
183
|
|
|
} |
184
|
|
|
} |
185
|
|
|
} |
186
|
|
|
|
187
|
|
|
$links[] = $nuLink; |
188
|
|
|
} |
189
|
|
|
|
190
|
|
|
$odata = new ODataEntry(); |
191
|
|
|
$odata->resourceSetName = $resourceSet->getName(); |
192
|
|
|
$odata->id = $absoluteUri; |
193
|
|
|
$odata->title = $title; |
194
|
|
|
$odata->type = $type; |
195
|
|
|
$odata->propertyContent = $propertyContent; |
196
|
|
|
$odata->isMediaLinkEntry = $resourceType->isMediaLinkEntry(); |
197
|
|
|
$odata->editLink = $relativeUri; |
198
|
|
|
$odata->mediaLink = $mediaLink; |
199
|
|
|
$odata->mediaLinks = $mediaLinks; |
200
|
|
|
$odata->links = $links; |
201
|
|
|
|
202
|
|
|
$newCount = count($this->lightStack); |
203
|
|
|
assert($newCount == $stackCount, "Should have $stackCount elements in stack, have $newCount elements"); |
204
|
|
|
array_pop($this->lightStack); |
205
|
|
|
return $odata; |
206
|
|
|
} |
207
|
|
|
|
208
|
|
|
/** |
209
|
|
|
* Write top level feed element. |
210
|
|
|
* |
211
|
|
|
* @param array &$entryObjects Array of entry resources to be written |
212
|
|
|
* |
213
|
|
|
* @return ODataFeed |
214
|
|
|
*/ |
215
|
|
|
public function writeTopLevelElements(&$entryObjects) |
216
|
|
|
{ |
217
|
|
|
assert(is_array($entryObjects), '!is_array($entryObjects)'); |
218
|
|
|
|
219
|
|
View Code Duplication |
if (0 == count($this->lightStack)) { |
|
|
|
|
220
|
|
|
$typeName = $this->getRequest()->getTargetResourceType()->getName(); |
221
|
|
|
array_push($this->lightStack, [$typeName, $typeName]); |
222
|
|
|
} |
223
|
|
|
$setName = $this->getRequest()->getTargetResourceSetWrapper()->getName(); |
224
|
|
|
|
225
|
|
|
|
226
|
|
|
$title = $this->getRequest()->getContainerName(); |
227
|
|
|
$relativeUri = $this->getRequest()->getIdentifier(); |
228
|
|
|
$absoluteUri = $this->getRequest()->getRequestUrl()->getUrlAsString(); |
229
|
|
|
|
230
|
|
|
$selfLink = new ODataLink(); |
231
|
|
|
$selfLink->name = 'self'; |
232
|
|
|
$selfLink->title = $relativeUri; |
233
|
|
|
$selfLink->url = $relativeUri; |
234
|
|
|
|
235
|
|
|
$odata = new ODataFeed(); |
236
|
|
|
$odata->title = $title; |
237
|
|
|
$odata->id = $absoluteUri; |
238
|
|
|
$odata->selfLink = $selfLink; |
239
|
|
|
|
240
|
|
|
if ($this->getRequest()->queryType == QueryType::ENTITIES_WITH_COUNT()) { |
241
|
|
|
$odata->rowCount = $this->getRequest()->getCountValue(); |
242
|
|
|
} |
243
|
|
|
foreach ($entryObjects as $entry) { |
244
|
|
|
$odata->entries[] = $this->writeTopLevelElement($entry); |
245
|
|
|
} |
246
|
|
|
|
247
|
|
|
|
248
|
|
|
if ($this->needNextPageLink(count($entryObjects))) { |
249
|
|
|
$stackSegment = $setName; |
250
|
|
|
$lastObject = end($entryObjects); |
251
|
|
|
$segment = $this->getNextLinkUri($lastObject, $absoluteUri); |
252
|
|
|
$nextLink = new ODataLink(); |
253
|
|
|
$nextLink->name = ODataConstants::ATOM_LINK_NEXT_ATTRIBUTE_STRING; |
254
|
|
|
$nextLink->url = rtrim($this->absoluteServiceUri, '/') . '/' . $stackSegment . $segment; |
255
|
|
|
$odata->nextPageLink = $nextLink; |
256
|
|
|
} |
257
|
|
|
|
258
|
|
|
return $odata; |
259
|
|
|
} |
260
|
|
|
|
261
|
|
|
/** |
262
|
|
|
* Write top level url element. |
263
|
|
|
* |
264
|
|
|
* @param mixed $entryObject The entry resource whose url to be written |
265
|
|
|
* |
266
|
|
|
* @return ODataURL |
267
|
|
|
*/ |
268
|
|
|
public function writeUrlElement($entryObject) |
269
|
|
|
{ |
270
|
|
|
$url = new ODataURL(); |
271
|
|
|
if (!is_null($entryObject)) { |
272
|
|
|
$currentResourceType = $this->getCurrentResourceSetWrapper()->getResourceType(); |
273
|
|
|
$relativeUri = $this->getEntryInstanceKey( |
274
|
|
|
$entryObject, |
275
|
|
|
$currentResourceType, |
276
|
|
|
$this->getCurrentResourceSetWrapper()->getName() |
277
|
|
|
); |
278
|
|
|
|
279
|
|
|
$url->url = rtrim($this->absoluteServiceUri, '/') . '/' . $relativeUri; |
280
|
|
|
} |
281
|
|
|
|
282
|
|
|
return $url; |
283
|
|
|
} |
284
|
|
|
|
285
|
|
|
/** |
286
|
|
|
* Write top level url collection. |
287
|
|
|
* |
288
|
|
|
* @param array $entryObjects Array of entry resources |
289
|
|
|
* whose url to be written |
290
|
|
|
* |
291
|
|
|
* @return ODataURLCollection |
292
|
|
|
*/ |
293
|
|
|
public function writeUrlElements($entryObjects) |
294
|
|
|
{ |
295
|
|
|
$urls = new ODataURLCollection(); |
296
|
|
|
if (!empty($entryObjects)) { |
297
|
|
|
$i = 0; |
298
|
|
|
foreach ($entryObjects as $entryObject) { |
299
|
|
|
$urls->urls[$i] = $this->writeUrlElement($entryObject); |
300
|
|
|
++$i; |
301
|
|
|
} |
302
|
|
|
|
303
|
|
|
if ($i > 0 && $this->needNextPageLink(count($entryObjects))) { |
304
|
|
|
$stackSegment = $this->getRequest()->getTargetResourceSetWrapper()->getName(); |
305
|
|
|
$lastObject = end($entryObjects); |
306
|
|
|
$segment = $this->getNextLinkUri($lastObject, $this->getRequest()->getRequestUrl()->getUrlAsString()); |
307
|
|
|
$nextLink = new ODataLink(); |
308
|
|
|
$nextLink->name = ODataConstants::ATOM_LINK_NEXT_ATTRIBUTE_STRING; |
309
|
|
|
$nextLink->url = rtrim($this->absoluteServiceUri, '/') . '/' . $stackSegment . $segment; |
310
|
|
|
$urls->nextPageLink = $nextLink; |
311
|
|
|
} |
312
|
|
|
} |
313
|
|
|
|
314
|
|
|
if ($this->getRequest()->queryType == QueryType::ENTITIES_WITH_COUNT()) { |
315
|
|
|
$urls->count = $this->getRequest()->getCountValue(); |
316
|
|
|
} |
317
|
|
|
|
318
|
|
|
return $urls; |
319
|
|
|
} |
320
|
|
|
|
321
|
|
|
/** |
322
|
|
|
* Write top level complex resource. |
323
|
|
|
* |
324
|
|
|
* @param mixed &$complexValue The complex object to be |
325
|
|
|
* written |
326
|
|
|
* @param string $propertyName The name of the |
327
|
|
|
* complex property |
328
|
|
|
* @param ResourceType &$resourceType Describes the type of |
329
|
|
|
* complex object |
330
|
|
|
* |
331
|
|
|
* @return ODataPropertyContent |
332
|
|
|
* @codeCoverageIgnore |
333
|
|
|
*/ |
334
|
|
|
public function writeTopLevelComplexObject(&$complexValue, $propertyName, ResourceType &$resourceType) |
335
|
|
|
{ |
336
|
|
|
// TODO: Figure out if we need to bother implementing this |
337
|
|
|
} |
338
|
|
|
|
339
|
|
|
/** |
340
|
|
|
* Write top level bag resource. |
341
|
|
|
* |
342
|
|
|
* @param mixed &$BagValue The bag object to be |
343
|
|
|
* written |
344
|
|
|
* @param string $propertyName The name of the |
345
|
|
|
* bag property |
346
|
|
|
* @param ResourceType &$resourceType Describes the type of |
347
|
|
|
* bag object |
348
|
|
|
* @codeCoverageIgnore |
349
|
|
|
* @return ODataPropertyContent |
350
|
|
|
*/ |
351
|
|
|
public function writeTopLevelBagObject(&$BagValue, $propertyName, ResourceType &$resourceType) |
352
|
|
|
{ |
353
|
|
|
// TODO: Figure out if we need to bother implementing this |
354
|
|
|
} |
355
|
|
|
|
356
|
|
|
/** |
357
|
|
|
* Write top level primitive value. |
358
|
|
|
* |
359
|
|
|
* @param mixed &$primitiveValue The primitve value to be |
360
|
|
|
* written |
361
|
|
|
* @param ResourceProperty &$resourceProperty Resource property |
362
|
|
|
* describing the |
363
|
|
|
* primitive property |
364
|
|
|
* to be written |
365
|
|
|
* @codeCoverageIgnore |
366
|
|
|
* @return ODataPropertyContent |
367
|
|
|
*/ |
368
|
|
|
public function writeTopLevelPrimitive(&$primitiveValue, ResourceProperty &$resourceProperty = null) |
369
|
|
|
{ |
370
|
|
|
// TODO: Figure out if we need to bother implementing this |
371
|
|
|
} |
372
|
|
|
|
373
|
|
|
/** |
374
|
|
|
* Gets reference to the request submitted by client. |
375
|
|
|
* |
376
|
|
|
* @return RequestDescription |
377
|
|
|
*/ |
378
|
|
|
public function getRequest() |
379
|
|
|
{ |
380
|
|
|
assert(null != $this->request, 'Request not yet set'); |
381
|
|
|
|
382
|
|
|
return $this->request; |
383
|
|
|
} |
384
|
|
|
|
385
|
|
|
/** |
386
|
|
|
* Sets reference to the request submitted by client. |
387
|
|
|
* |
388
|
|
|
* @param RequestDescription $request |
389
|
|
|
*/ |
390
|
|
|
public function setRequest(RequestDescription $request) |
391
|
|
|
{ |
392
|
|
|
$this->request = $request; |
393
|
|
|
$this->stack->setRequest($request); |
394
|
|
|
} |
395
|
|
|
|
396
|
|
|
/** |
397
|
|
|
* Gets the data service instance. |
398
|
|
|
* |
399
|
|
|
* @return IService |
400
|
|
|
*/ |
401
|
|
|
public function getService() |
402
|
|
|
{ |
403
|
|
|
return $this->service; |
404
|
|
|
} |
405
|
|
|
|
406
|
|
|
/** |
407
|
|
|
* Gets the segment stack instance. |
408
|
|
|
* |
409
|
|
|
* @return SegmentStack |
410
|
|
|
*/ |
411
|
|
|
public function getStack() |
412
|
|
|
{ |
413
|
|
|
return $this->stack; |
414
|
|
|
} |
415
|
|
|
|
416
|
|
|
protected function getEntryInstanceKey($entityInstance, ResourceType $resourceType, $containerName) |
417
|
|
|
{ |
418
|
|
|
$typeName = $resourceType->getName(); |
419
|
|
|
$keyProperties = $resourceType->getKeyProperties(); |
420
|
|
|
assert(count($keyProperties) != 0, 'count($keyProperties) == 0'); |
421
|
|
|
$keyString = $containerName . '('; |
422
|
|
|
$comma = null; |
423
|
|
|
foreach ($keyProperties as $keyName => $resourceProperty) { |
424
|
|
|
$keyType = $resourceProperty->getInstanceType(); |
425
|
|
|
assert($keyType instanceof IType, '$keyType not instanceof IType'); |
426
|
|
|
$keyName = $resourceProperty->getName(); |
427
|
|
|
$keyValue = $entityInstance->$keyName; |
428
|
|
|
if (!isset($keyValue)) { |
429
|
|
|
throw ODataException::createInternalServerError( |
430
|
|
|
Messages::badQueryNullKeysAreNotSupported($typeName, $keyName) |
431
|
|
|
); |
432
|
|
|
} |
433
|
|
|
|
434
|
|
|
$keyValue = $keyType->convertToOData($keyValue); |
435
|
|
|
$keyString .= $comma . $keyName . '=' . $keyValue; |
436
|
|
|
$comma = ','; |
437
|
|
|
} |
438
|
|
|
|
439
|
|
|
$keyString .= ')'; |
440
|
|
|
|
441
|
|
|
return $keyString; |
442
|
|
|
} |
443
|
|
|
|
444
|
|
|
/** |
445
|
|
|
* @param $entryObject |
446
|
|
|
* @param $type |
447
|
|
|
* @param $relativeUri |
448
|
|
|
* @param $resourceType |
449
|
|
|
* @return array |
450
|
|
|
*/ |
451
|
|
|
protected function writeMediaData($entryObject, $type, $relativeUri, ResourceType $resourceType) |
452
|
|
|
{ |
453
|
|
|
$context = $this->getService()->getOperationContext(); |
454
|
|
|
$streamProviderWrapper = $this->getService()->getStreamProviderWrapper(); |
455
|
|
|
assert(null != $streamProviderWrapper, "Retrieved stream provider must not be null"); |
456
|
|
|
|
457
|
|
|
$mediaLink = null; |
458
|
|
|
if ($resourceType->isMediaLinkEntry()) { |
459
|
|
|
$eTag = $streamProviderWrapper->getStreamETag2($entryObject, null, $context); |
|
|
|
|
460
|
|
|
$mediaLink = new ODataMediaLink($type, '/$value', $relativeUri . '/$value', '*/*', $eTag); |
461
|
|
|
} |
462
|
|
|
$mediaLinks = []; |
463
|
|
|
if ($resourceType->hasNamedStream()) { |
464
|
|
|
$namedStreams = $resourceType->getAllNamedStreams(); |
465
|
|
|
foreach ($namedStreams as $streamTitle => $resourceStreamInfo) { |
466
|
|
|
$readUri = $streamProviderWrapper->getReadStreamUri2( |
|
|
|
|
467
|
|
|
$entryObject, |
468
|
|
|
$resourceStreamInfo, |
469
|
|
|
$context, |
470
|
|
|
$relativeUri |
471
|
|
|
); |
472
|
|
|
$mediaContentType = $streamProviderWrapper->getStreamContentType2( |
|
|
|
|
473
|
|
|
$entryObject, |
474
|
|
|
$resourceStreamInfo, |
475
|
|
|
$context |
476
|
|
|
); |
477
|
|
|
$eTag = $streamProviderWrapper->getStreamETag2( |
|
|
|
|
478
|
|
|
$entryObject, |
479
|
|
|
$resourceStreamInfo, |
480
|
|
|
$context |
481
|
|
|
); |
482
|
|
|
|
483
|
|
|
$nuLink = new ODataMediaLink($streamTitle, $readUri, $readUri, $mediaContentType, $eTag); |
484
|
|
|
$mediaLinks[] = $nuLink; |
485
|
|
|
} |
486
|
|
|
} |
487
|
|
|
return [$mediaLink, $mediaLinks]; |
488
|
|
|
} |
489
|
|
|
|
490
|
|
|
/** |
491
|
|
|
* Gets collection of projection nodes under the current node. |
492
|
|
|
* |
493
|
|
|
* @return ProjectionNode[]|ExpandedProjectionNode[]|null List of nodes |
494
|
|
|
* describing projections for the current segment, If this method returns |
495
|
|
|
* null it means no projections are to be applied and the entire resource |
496
|
|
|
* for the current segment should be serialized, If it returns non-null |
497
|
|
|
* only the properties described by the returned projection segments should |
498
|
|
|
* be serialized |
499
|
|
|
*/ |
500
|
|
|
protected function getProjectionNodes() |
501
|
|
|
{ |
502
|
|
|
$expandedProjectionNode = $this->getCurrentExpandedProjectionNode(); |
503
|
|
|
if (is_null($expandedProjectionNode) || $expandedProjectionNode->canSelectAllProperties()) { |
504
|
|
|
return null; |
505
|
|
|
} |
506
|
|
|
|
507
|
|
|
return $expandedProjectionNode->getChildNodes(); |
508
|
|
|
} |
509
|
|
|
|
510
|
|
|
/** |
511
|
|
|
* Find a 'ExpandedProjectionNode' instance in the projection tree |
512
|
|
|
* which describes the current segment. |
513
|
|
|
* |
514
|
|
|
* @return ExpandedProjectionNode|null |
515
|
|
|
*/ |
516
|
|
|
protected function getCurrentExpandedProjectionNode() |
517
|
|
|
{ |
518
|
|
|
$expandedProjectionNode = $this->getRequest()->getRootProjectionNode(); |
519
|
|
|
if (is_null($expandedProjectionNode)) { |
520
|
|
|
return null; |
521
|
|
|
} else { |
522
|
|
|
$segmentNames = $this->getStack()->getSegmentNames(); |
523
|
|
|
$depth = count($segmentNames); |
524
|
|
|
// $depth == 1 means serialization of root entry |
525
|
|
|
//(the resource identified by resource path) is going on, |
526
|
|
|
//so control won't get into the below for loop. |
527
|
|
|
//we will directly return the root node, |
528
|
|
|
//which is 'ExpandedProjectionNode' |
529
|
|
|
// for resource identified by resource path. |
530
|
|
|
if (0 != $depth) { |
531
|
|
|
for ($i = 1; $i < $depth; ++$i) { |
532
|
|
|
$expandedProjectionNode = $expandedProjectionNode->findNode($segmentNames[$i]); |
533
|
|
|
assert(!is_null($expandedProjectionNode), 'is_null($expandedProjectionNode)'); |
534
|
|
|
assert( |
535
|
|
|
$expandedProjectionNode instanceof ExpandedProjectionNode, |
536
|
|
|
'$expandedProjectionNode not instanceof ExpandedProjectionNode' |
537
|
|
|
); |
538
|
|
|
} |
539
|
|
|
} |
540
|
|
|
} |
541
|
|
|
|
542
|
|
|
return $expandedProjectionNode; |
543
|
|
|
} |
544
|
|
|
|
545
|
|
|
/** |
546
|
|
|
* Check whether to expand a navigation property or not. |
547
|
|
|
* |
548
|
|
|
* @param string $navigationPropertyName Name of naviagtion property in question |
549
|
|
|
* |
550
|
|
|
* @return bool True if the given navigation should be |
551
|
|
|
* explanded otherwise false |
552
|
|
|
*/ |
553
|
|
|
protected function shouldExpandSegment($navigationPropertyName) |
554
|
|
|
{ |
555
|
|
|
$expandedProjectionNode = $this->getCurrentExpandedProjectionNode(); |
556
|
|
|
if (is_null($expandedProjectionNode)) { |
557
|
|
|
return false; |
558
|
|
|
} |
559
|
|
|
|
560
|
|
|
$expandedProjectionNode = $expandedProjectionNode->findNode($navigationPropertyName); |
561
|
|
|
|
562
|
|
|
// null is a valid input to an instanceof call as of PHP 5.6 - will always return false |
563
|
|
|
return $expandedProjectionNode instanceof ExpandedProjectionNode; |
564
|
|
|
} |
565
|
|
|
|
566
|
|
|
/** |
567
|
|
|
* Wheter next link is needed for the current resource set (feed) |
568
|
|
|
* being serialized. |
569
|
|
|
* |
570
|
|
|
* @param int $resultSetCount Number of entries in the current |
571
|
|
|
* resource set |
572
|
|
|
* |
573
|
|
|
* @return bool true if the feed must have a next page link |
574
|
|
|
*/ |
575
|
|
|
protected function needNextPageLink($resultSetCount) |
576
|
|
|
{ |
577
|
|
|
$currentResourceSet = $this->getCurrentResourceSetWrapper(); |
578
|
|
|
$recursionLevel = count($this->getStack()->getSegmentNames()); |
579
|
|
|
//$this->assert($recursionLevel != 0, '$recursionLevel != 0'); |
|
|
|
|
580
|
|
|
$pageSize = $currentResourceSet->getResourceSetPageSize(); |
581
|
|
|
|
582
|
|
|
if (1 == $recursionLevel) { |
583
|
|
|
//presence of $top option affect next link for root container |
584
|
|
|
$topValueCount = $this->getRequest()->getTopOptionCount(); |
585
|
|
|
if (!is_null($topValueCount) && ($topValueCount <= $pageSize)) { |
586
|
|
|
return false; |
587
|
|
|
} |
588
|
|
|
} |
589
|
|
|
return $resultSetCount == $pageSize; |
590
|
|
|
} |
591
|
|
|
|
592
|
|
|
/** |
593
|
|
|
* Resource set wrapper for the resource being serialized. |
594
|
|
|
* |
595
|
|
|
* @return ResourceSetWrapper |
596
|
|
|
*/ |
597
|
|
|
protected function getCurrentResourceSetWrapper() |
598
|
|
|
{ |
599
|
|
|
$segmentWrappers = $this->getStack()->getSegmentWrappers(); |
600
|
|
|
$count = count($segmentWrappers); |
601
|
|
|
|
602
|
|
|
return 0 == $count ? $this->getRequest()->getTargetResourceSetWrapper() : $segmentWrappers[$count - 1]; |
603
|
|
|
} |
604
|
|
|
|
605
|
|
|
/** |
606
|
|
|
* Get next page link from the given entity instance. |
607
|
|
|
* |
608
|
|
|
* @param mixed &$lastObject Last object serialized to be |
609
|
|
|
* used for generating $skiptoken |
610
|
|
|
* @param string $absoluteUri Absolute response URI |
611
|
|
|
* |
612
|
|
|
* @return string for the link for next page |
613
|
|
|
*/ |
614
|
|
|
protected function getNextLinkUri(&$lastObject, $absoluteUri) |
|
|
|
|
615
|
|
|
{ |
616
|
|
|
$currentExpandedProjectionNode = $this->getCurrentExpandedProjectionNode(); |
617
|
|
|
$internalOrderByInfo = $currentExpandedProjectionNode->getInternalOrderByInfo(); |
618
|
|
|
$skipToken = $internalOrderByInfo->buildSkipTokenValue($lastObject); |
619
|
|
|
assert(!is_null($skipToken), '!is_null($skipToken)'); |
620
|
|
|
$skipToken = '?$skip='.$skipToken; |
621
|
|
|
return $skipToken; |
622
|
|
|
} |
623
|
|
|
} |
624
|
|
|
|
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.