1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
namespace POData\Providers\Metadata; |
4
|
|
|
|
5
|
|
|
use AlgoWeb\ODataMetadata\IsOK; |
6
|
|
|
use AlgoWeb\ODataMetadata\MetadataManager; |
7
|
|
|
use AlgoWeb\ODataMetadata\MetadataV3\edm\TComplexTypeType; |
8
|
|
|
use AlgoWeb\ODataMetadata\MetadataV3\edm\TEntityTypeType; |
9
|
|
|
use Illuminate\Support\Str; |
10
|
|
|
use POData\Common\InvalidOperationException; |
11
|
|
|
use POData\Common\NotImplementedException; |
12
|
|
|
use POData\Providers\Metadata\Type\IType; |
13
|
|
|
use POData\Providers\Metadata\Type\TypeCode; |
14
|
|
|
|
15
|
|
|
/** |
16
|
|
|
* Class SimpleMetadataProvider. |
17
|
|
|
*/ |
18
|
|
|
class SimpleMetadataProvider implements IMetadataProvider |
19
|
|
|
{ |
20
|
|
|
public $OdataEntityMap = []; |
|
|
|
|
21
|
|
|
protected $resourceSets = []; |
22
|
|
|
protected $resourceTypes = []; |
23
|
|
|
protected $associationSets = []; |
24
|
|
|
protected $containerName; |
25
|
|
|
protected $namespaceName; |
26
|
|
|
private $metadataManager; |
27
|
|
|
private $typeSetMapping = []; |
28
|
|
|
protected $singletons = []; |
29
|
|
|
|
30
|
|
|
/** |
31
|
|
|
* @param string $containerName container name for the datasource |
32
|
|
|
* @param string $namespaceName namespace for the datasource |
33
|
|
|
*/ |
34
|
|
|
public function __construct($containerName, $namespaceName) |
35
|
|
|
{ |
36
|
|
|
$this->containerName = $containerName; |
37
|
|
|
$this->namespaceName = $namespaceName; |
38
|
|
|
$this->metadataManager = new MetadataManager($namespaceName, $containerName); |
39
|
|
|
} |
40
|
|
|
|
41
|
|
|
//Begin Implementation of IMetadataProvider |
42
|
|
|
|
43
|
|
|
public function getXML() |
|
|
|
|
44
|
|
|
{ |
45
|
|
|
return $this->metadataManager->getEdmxXML(); |
46
|
|
|
} |
47
|
|
|
|
48
|
|
|
/** |
49
|
|
|
* get the Container name for the data source. |
50
|
|
|
* |
51
|
|
|
* @return string container name |
52
|
|
|
*/ |
53
|
|
|
public function getContainerName() |
54
|
|
|
{ |
55
|
|
|
return $this->containerName; |
56
|
|
|
} |
57
|
|
|
|
58
|
|
|
/** |
59
|
|
|
* get Namespace name for the data source. |
60
|
|
|
* |
61
|
|
|
* @return string namespace |
62
|
|
|
*/ |
63
|
|
|
public function getContainerNamespace() |
64
|
|
|
{ |
65
|
|
|
return $this->namespaceName; |
66
|
|
|
} |
67
|
|
|
|
68
|
|
|
/** |
69
|
|
|
* get all entity set information. |
70
|
|
|
* |
71
|
|
|
* @return ResourceSet[] |
72
|
|
|
*/ |
73
|
|
|
public function getResourceSets($params = null) |
74
|
|
|
{ |
75
|
|
|
$parameters = []; |
76
|
|
|
if (is_string($params)) { |
77
|
|
|
$parameters[] = $params; |
78
|
|
|
} elseif (isset($params) && !is_array($params)) { |
79
|
|
|
throw new \ErrorException('Input parameter must be absent, null, string or array'); |
80
|
|
|
} else { |
81
|
|
|
$parameters = $params; |
82
|
|
|
} |
83
|
|
|
if (!is_array($parameters) || 0 == count($parameters)) { |
84
|
|
|
return array_values($this->resourceSets); |
85
|
|
|
} |
86
|
|
|
assert(is_array($parameters)); |
87
|
|
|
$return = []; |
88
|
|
|
$counter = 0; |
89
|
|
|
foreach ($this->resourceSets as $resource) { |
90
|
|
|
$resName = $resource->getName(); |
91
|
|
|
if (in_array($resName, $parameters)) { |
92
|
|
|
$return[] = $resource; |
93
|
|
|
$counter++; |
94
|
|
|
} |
95
|
|
|
} |
96
|
|
|
assert($counter == count($return)); |
97
|
|
|
|
98
|
|
|
return $return; |
99
|
|
|
} |
100
|
|
|
|
101
|
|
|
/** |
102
|
|
|
* get all resource types in the data source. |
103
|
|
|
* |
104
|
|
|
* @return ResourceType[] |
105
|
|
|
*/ |
106
|
|
|
public function getTypes() |
107
|
|
|
{ |
108
|
|
|
return array_values($this->resourceTypes); |
109
|
|
|
} |
110
|
|
|
|
111
|
|
|
/** |
112
|
|
|
* get a resource set based on the specified resource set name. |
113
|
|
|
* |
114
|
|
|
* @param string $name Name of the resource set |
115
|
|
|
* |
116
|
|
|
* @return ResourceSet|null resource set with the given name if found else NULL |
117
|
|
|
*/ |
118
|
|
|
public function resolveResourceSet($name) |
119
|
|
|
{ |
120
|
|
|
if (array_key_exists($name, $this->resourceSets)) { |
121
|
|
|
return $this->resourceSets[$name]; |
122
|
|
|
} |
123
|
|
|
return null; |
124
|
|
|
} |
125
|
|
|
|
126
|
|
|
/** |
127
|
|
|
* get a resource type based on the resource type name. |
128
|
|
|
* |
129
|
|
|
* @param string $name Name of the resource type |
130
|
|
|
* |
131
|
|
|
* @return ResourceType|null resource type with the given resource type name if found else NULL |
132
|
|
|
*/ |
133
|
|
|
public function resolveResourceType($name) |
134
|
|
|
{ |
135
|
|
|
if (array_key_exists($name, $this->resourceTypes)) { |
136
|
|
|
return $this->resourceTypes[$name]; |
137
|
|
|
} |
138
|
|
|
return null; |
139
|
|
|
} |
140
|
|
|
|
141
|
|
|
/** |
142
|
|
|
* get a singelton based on the specified singleton name. |
143
|
|
|
* |
144
|
|
|
* @param string $name Name of the resource set |
145
|
|
|
* |
146
|
|
|
* @return ResourceFunctionType|null singleton with the given name if found else NULL |
147
|
|
|
*/ |
148
|
|
|
public function resolveSingleton($name) |
149
|
|
|
{ |
150
|
|
|
if (array_key_exists($name, $this->singletons)) { |
151
|
|
|
return $this->singletons[$name]; |
152
|
|
|
} |
153
|
|
|
return null; |
154
|
|
|
} |
155
|
|
|
|
156
|
|
|
/** |
157
|
|
|
* get a resource set based on the specified resource association set name. |
158
|
|
|
* |
159
|
|
|
* @param string $name Name of the resource assocation set |
160
|
|
|
* |
161
|
|
|
* @return ResourceAssociationSet|null resource association set with the given name if found else NULL |
162
|
|
|
*/ |
163
|
|
|
public function resolveAssociationSet($name) |
164
|
|
|
{ |
165
|
|
|
if (array_key_exists($name, $this->associationSets)) { |
166
|
|
|
return $this->associationSets[$name]; |
167
|
|
|
} |
168
|
|
|
return null; |
169
|
|
|
} |
170
|
|
|
|
171
|
|
|
/* |
172
|
|
|
* Get number of association sets hooked up |
173
|
|
|
*/ |
174
|
|
|
public function getAssociationCount() |
175
|
|
|
{ |
176
|
|
|
return count($this->associationSets); |
177
|
|
|
} |
178
|
|
|
|
179
|
|
|
/** |
180
|
|
|
* The method must return a collection of all the types derived from |
181
|
|
|
* $resourceType The collection returned should NOT include the type |
182
|
|
|
* passed in as a parameter. |
183
|
|
|
* |
184
|
|
|
* @param ResourceEntityType $resourceType Resource to get derived resource types from |
185
|
|
|
* |
186
|
|
|
* @return ResourceType[] |
187
|
|
|
*/ |
188
|
|
|
public function getDerivedTypes(ResourceEntityType $resourceType) |
189
|
|
|
{ |
190
|
|
|
return []; |
191
|
|
|
} |
192
|
|
|
|
193
|
|
|
/** |
194
|
|
|
* @param ResourceType $resourceType Resource to check for derived resource types |
|
|
|
|
195
|
|
|
* |
196
|
|
|
* @return bool true if $resourceType represents an Entity Type which has derived Entity Types, else false |
197
|
|
|
*/ |
198
|
|
|
public function hasDerivedTypes(ResourceEntityType $resourceType) |
199
|
|
|
{ |
200
|
|
|
return false; |
201
|
|
|
} |
202
|
|
|
|
203
|
|
|
//End Implementation of IMetadataProvider |
204
|
|
|
|
205
|
|
|
/** |
206
|
|
|
* Gets the ResourceAssociationSet instance for the given source |
207
|
|
|
* association end. |
208
|
|
|
* |
209
|
|
|
* @param ResourceSet $sourceResourceSet Resource set |
210
|
|
|
* of the source |
211
|
|
|
* association end |
212
|
|
|
* @param ResourceType $sourceResourceType Resource type of the source |
|
|
|
|
213
|
|
|
* association end |
214
|
|
|
* @param ResourceProperty $targetResourceProperty Resource property of |
215
|
|
|
* the source |
216
|
|
|
* association end |
217
|
|
|
* |
218
|
|
|
* @return ResourceAssociationSet|null |
219
|
|
|
*/ |
220
|
|
|
public function getResourceAssociationSet( |
221
|
|
|
ResourceSet $sourceResourceSet, |
222
|
|
|
ResourceEntityType $sourceResourceType, |
223
|
|
|
ResourceProperty $targetResourceProperty |
|
|
|
|
224
|
|
|
) { |
225
|
|
|
//e.g. |
226
|
|
|
//ResourceSet => Representing 'Customers' entity set |
227
|
|
|
//ResourceType => Representing'Customer' entity type |
228
|
|
|
//ResourceProperty => Representing 'Orders' property |
229
|
|
|
//We have created ResourceAssoicationSet while adding |
230
|
|
|
//ResourceSetReference or ResourceReference |
231
|
|
|
//and kept in $this->associationSets |
232
|
|
|
//$metadata->addResourceSetReferenceProperty( |
233
|
|
|
// $customersEntityType, |
234
|
|
|
// 'Orders', |
235
|
|
|
// $ordersResourceSet |
236
|
|
|
// ); |
237
|
|
|
|
238
|
|
|
$targetResourceSet = $targetResourceProperty->getResourceType()->getCustomState(); |
239
|
|
|
if (is_null($targetResourceSet)) { |
240
|
|
|
throw new InvalidOperationException( |
241
|
|
|
'Failed to retrieve the custom state from ' . $targetResourceProperty->getResourceType()->getName() |
242
|
|
|
); |
243
|
|
|
} |
244
|
|
|
|
245
|
|
|
//Customer_Orders_Orders, Order_Customer_Customers |
246
|
|
|
$key = ResourceAssociationSet::keyName( |
247
|
|
|
$sourceResourceType, |
248
|
|
|
$targetResourceProperty->getName(), |
249
|
|
|
$targetResourceSet |
250
|
|
|
); |
251
|
|
|
|
252
|
|
|
$associationSet = array_key_exists($key, $this->associationSets) ? $this->associationSets[$key] : null; |
253
|
|
|
assert( |
254
|
|
|
null == $associationSet || $associationSet instanceof ResourceAssociationSet, |
255
|
|
|
"Retrieved resource assocation must be either null or an instance of ResourceAssociationSet" |
|
|
|
|
256
|
|
|
); |
257
|
|
|
return $associationSet; |
258
|
|
|
} |
259
|
|
|
|
260
|
|
|
/** |
261
|
|
|
* Add an entity type. |
262
|
|
|
* |
263
|
|
|
* @param \ReflectionClass $refClass reflection class of the entity |
264
|
|
|
* @param string $name name of the entity |
265
|
|
|
* @return ResourceType when the name is already in use |
|
|
|
|
266
|
|
|
* |
267
|
|
|
* @throws InvalidOperationException when the name is already in use |
268
|
|
|
* @internal param string $namespace namespace of the data source |
269
|
|
|
* |
270
|
|
|
*/ |
271
|
|
|
public function addEntityType(\ReflectionClass $refClass, $name) |
272
|
|
|
{ |
273
|
|
|
return $this->createResourceType($refClass, $name, ResourceTypeKind::ENTITY); |
274
|
|
|
} |
275
|
|
|
|
276
|
|
|
/** |
277
|
|
|
* @param \ReflectionClass $refClass |
278
|
|
|
* @param string $name |
279
|
|
|
* @param $typeKind |
280
|
|
|
* @return ResourceType |
|
|
|
|
281
|
|
|
* @throws InvalidOperationException |
282
|
|
|
* @internal param null|string $namespace |
283
|
|
|
* @internal param null|ResourceType $baseResourceType |
284
|
|
|
* |
285
|
|
|
*/ |
286
|
|
|
private function createResourceType( |
287
|
|
|
\ReflectionClass $refClass, |
288
|
|
|
$name, |
289
|
|
|
$typeKind |
290
|
|
|
) { |
291
|
|
|
if (array_key_exists($name, $this->resourceTypes)) { |
292
|
|
|
throw new InvalidOperationException('Type with same name already added'); |
293
|
|
|
} |
294
|
|
|
|
295
|
|
|
$type = null; |
296
|
|
|
if ($typeKind == ResourceTypeKind::ENTITY) { |
297
|
|
|
list($oet, $entitySet) = $this->metadataManager->addEntityType($name); |
298
|
|
|
assert($oet instanceof TEntityTypeType, "Entity type ".$name. " not successfully added"); |
|
|
|
|
299
|
|
|
$type = new ResourceEntityType($refClass, $oet, $this); |
300
|
|
|
$typeName = $type->getFullName(); |
301
|
|
|
$returnName = Str::plural($typeName); |
302
|
|
|
$this->OdataEntityMap[$typeName] = $oet; |
303
|
|
|
$this->typeSetMapping[$name] = $entitySet; |
304
|
|
|
$this->typeSetMapping[$typeName] = $entitySet; |
305
|
|
|
$this->typeSetMapping[$returnName] = $entitySet; |
306
|
|
|
} elseif ($typeKind == ResourceTypeKind::COMPLEX) { |
307
|
|
|
$complex = new TComplexTypeType(); |
308
|
|
|
$complex->setName($name); |
309
|
|
|
$type = new ResourceComplexType($refClass, $complex); |
310
|
|
|
} |
311
|
|
|
assert(null != $type, "Type variable must not be null"); |
|
|
|
|
312
|
|
|
|
313
|
|
|
$this->resourceTypes[$name] = $type; |
314
|
|
|
ksort($this->resourceTypes); |
315
|
|
|
|
316
|
|
|
return $type; |
317
|
|
|
} |
318
|
|
|
|
319
|
|
|
/** |
320
|
|
|
* Add a complex type. |
321
|
|
|
* |
322
|
|
|
* @param \ReflectionClass $refClass reflection class of the complex entity type |
323
|
|
|
* @param string $name name of the entity |
324
|
|
|
* @return ResourceType when the name is already in use |
|
|
|
|
325
|
|
|
* |
326
|
|
|
* @throws InvalidOperationException when the name is already in use |
327
|
|
|
* @internal param string $namespace namespace of the data source |
328
|
|
|
* @internal param ResourceType $baseResourceType base resource type |
329
|
|
|
* |
330
|
|
|
*/ |
331
|
|
|
public function addComplexType(\ReflectionClass $refClass, $name) |
332
|
|
|
{ |
333
|
|
|
return $this->createResourceType($refClass, $name, ResourceTypeKind::COMPLEX); |
334
|
|
|
} |
335
|
|
|
|
336
|
|
|
/** |
337
|
|
|
* @param string $name name of the resource set (now taken from resource type) |
338
|
|
|
* @param ResourceEntityType $resourceType resource type |
339
|
|
|
* |
340
|
|
|
* @throws InvalidOperationException |
341
|
|
|
* |
342
|
|
|
* @return ResourceSet |
343
|
|
|
*/ |
344
|
|
|
public function addResourceSet($name, ResourceEntityType $resourceType) |
345
|
|
|
{ |
346
|
|
|
$returnName = Str::plural($resourceType->getFullName()); |
347
|
|
|
if (array_key_exists($returnName, $this->resourceSets)) { |
348
|
|
|
throw new InvalidOperationException('Resource Set already added'); |
349
|
|
|
} |
350
|
|
|
|
351
|
|
|
$this->resourceSets[$returnName] = new ResourceSet($returnName, $resourceType); |
352
|
|
|
|
353
|
|
|
//No support for multiple ResourceSet with same EntityType |
354
|
|
|
//So keeping reference to the 'ResourceSet' with the entity type |
355
|
|
|
$resourceType->setCustomState($this->resourceSets[$returnName]); |
356
|
|
|
ksort($this->resourceSets); |
357
|
|
|
|
358
|
|
|
return $this->resourceSets[$returnName]; |
359
|
|
|
} |
360
|
|
|
|
361
|
|
|
/** |
362
|
|
|
* To add a Key-primitive property to a resource (Complex/Entity). |
363
|
|
|
* |
364
|
|
|
* @param ResourceType $resourceType resource type to which key property |
365
|
|
|
* is to be added |
366
|
|
|
* @param string $name name of the key property |
367
|
|
|
* @param TypeCode $typeCode type of the key property |
368
|
|
|
*/ |
369
|
|
|
public function addKeyProperty($resourceType, $name, $typeCode) |
370
|
|
|
{ |
371
|
|
|
$this->addPrimitivePropertyInternal($resourceType, $name, $typeCode, true); |
372
|
|
|
} |
373
|
|
|
|
374
|
|
|
/** |
375
|
|
|
* To add a Key/NonKey-primitive property to a resource (complex/entity). |
376
|
|
|
* |
377
|
|
|
* @param ResourceType $resourceType Resource type |
378
|
|
|
* @param string $name name of the property |
379
|
|
|
* @param TypeCode $typeCode type of property |
380
|
|
|
* @param bool $isKey property is key or not |
381
|
|
|
* @param bool $isBag property is bag or not |
382
|
|
|
* @param bool $isETagProperty property is etag or not |
383
|
|
|
*/ |
384
|
|
|
private function addPrimitivePropertyInternal( |
385
|
|
|
$resourceType, |
386
|
|
|
$name, |
387
|
|
|
$typeCode, |
388
|
|
|
$isKey = false, |
389
|
|
|
$isBag = false, |
390
|
|
|
$isETagProperty = false, |
391
|
|
|
$defaultValue = null, |
392
|
|
|
$nullable = false |
393
|
|
|
) { |
394
|
|
|
$this->checkInstanceProperty($name, $resourceType); |
395
|
|
|
|
396
|
|
|
// check that property and resource name don't up and collide - would violate OData spec |
397
|
|
|
if (strtolower($name) == strtolower($resourceType->getName())) { |
398
|
|
|
throw new InvalidOperationException( |
399
|
|
|
'Property name must be different from resource name.' |
400
|
|
|
); |
401
|
|
|
} |
402
|
|
|
|
403
|
|
|
$primitiveResourceType = ResourceType::getPrimitiveResourceType($typeCode); |
|
|
|
|
404
|
|
|
|
405
|
|
|
if ($isETagProperty && $isBag) { |
406
|
|
|
throw new InvalidOperationException( |
407
|
|
|
'Only primitve property can be etag property, bag property cannot be etag property.' |
408
|
|
|
); |
409
|
|
|
} |
410
|
|
|
|
411
|
|
|
$kind = $isKey ? ResourcePropertyKind::PRIMITIVE | ResourcePropertyKind::KEY : ResourcePropertyKind::PRIMITIVE; |
412
|
|
|
if ($isBag) { |
413
|
|
|
$kind = $kind | ResourcePropertyKind::BAG; |
414
|
|
|
} |
415
|
|
|
|
416
|
|
|
if ($isETagProperty) { |
417
|
|
|
$kind = $kind | ResourcePropertyKind::ETAG; |
418
|
|
|
} |
419
|
|
|
|
420
|
|
|
$resourceProperty = new ResourceProperty($name, null, $kind, $primitiveResourceType); |
421
|
|
|
$resourceType->addProperty($resourceProperty); |
422
|
|
|
if (array_key_exists($resourceType->getFullName(), $this->OdataEntityMap)) { |
423
|
|
|
$this->metadataManager->addPropertyToEntityType( |
424
|
|
|
$this->OdataEntityMap[$resourceType->getFullName()], |
425
|
|
|
$name, |
426
|
|
|
$primitiveResourceType->getFullName(), |
427
|
|
|
$defaultValue, |
428
|
|
|
$nullable, |
429
|
|
|
$isKey |
430
|
|
|
); |
431
|
|
|
} |
432
|
|
|
} |
433
|
|
|
|
434
|
|
|
/** |
435
|
|
|
* @param string $name |
436
|
|
|
* @param ResourceType $resourceType |
437
|
|
|
* |
438
|
|
|
* @throws InvalidOperationException |
439
|
|
|
*/ |
440
|
|
|
private function checkInstanceProperty($name, ResourceType $resourceType) |
441
|
|
|
{ |
442
|
|
|
$instance = $resourceType->getInstanceType(); |
443
|
|
|
$hasMagicGetter = $instance instanceof IType || $instance->hasMethod('__get'); |
444
|
|
|
|
445
|
|
|
if (!$hasMagicGetter) { |
446
|
|
|
try { |
447
|
|
|
if ($instance instanceof \ReflectionClass) { |
448
|
|
|
$instance->getProperty($name); |
449
|
|
|
} |
450
|
|
|
} catch (\ReflectionException $exception) { |
451
|
|
|
throw new InvalidOperationException( |
452
|
|
|
'Can\'t add a property which does not exist on the instance type.' |
453
|
|
|
); |
454
|
|
|
} |
455
|
|
|
} |
456
|
|
|
} |
457
|
|
|
|
458
|
|
|
/** |
459
|
|
|
* To add a NonKey-primitive property (Complex/Entity). |
460
|
|
|
* |
461
|
|
|
* @param ResourceType $resourceType resource type to which key property |
462
|
|
|
* is to be added |
463
|
|
|
* @param string $name name of the key property |
464
|
|
|
* @param TypeCode $typeCode type of the key property |
465
|
|
|
* @param bool $isBag property is bag or not |
466
|
|
|
*/ |
467
|
|
View Code Duplication |
public function addPrimitiveProperty( |
|
|
|
|
468
|
|
|
$resourceType, |
469
|
|
|
$name, |
470
|
|
|
$typeCode, |
471
|
|
|
$isBag = false, |
472
|
|
|
$defaultValue = null, |
473
|
|
|
$nullable = false |
474
|
|
|
) { |
475
|
|
|
$this->addPrimitivePropertyInternal( |
476
|
|
|
$resourceType, |
477
|
|
|
$name, |
478
|
|
|
$typeCode, |
479
|
|
|
false, |
480
|
|
|
$isBag, |
481
|
|
|
false, |
482
|
|
|
$defaultValue, |
483
|
|
|
$nullable |
484
|
|
|
); |
485
|
|
|
} |
486
|
|
|
|
487
|
|
|
/** |
488
|
|
|
* To add a non-key etag property. |
489
|
|
|
* |
490
|
|
|
* @param ResourceType $resourceType resource type to which key property |
491
|
|
|
* is to be added |
492
|
|
|
* @param string $name name of the property |
493
|
|
|
* @param TypeCode $typeCode type of the etag property |
494
|
|
|
*/ |
495
|
|
View Code Duplication |
public function addETagProperty($resourceType, $name, $typeCode, $defaultValue = null, $nullable = false) |
|
|
|
|
496
|
|
|
{ |
497
|
|
|
$this->addPrimitivePropertyInternal( |
498
|
|
|
$resourceType, |
499
|
|
|
$name, |
500
|
|
|
$typeCode, |
501
|
|
|
false, |
502
|
|
|
false, |
503
|
|
|
true, |
504
|
|
|
$defaultValue, |
505
|
|
|
$nullable |
506
|
|
|
); |
507
|
|
|
} |
508
|
|
|
|
509
|
|
|
/** |
510
|
|
|
* To add a resource reference property. |
511
|
|
|
* |
512
|
|
|
* @param ResourceEntityType $resourceType The resource type to add the resource |
513
|
|
|
* reference property to |
514
|
|
|
* @param string $name The name of the property to add |
515
|
|
|
* @param ResourceSet $targetResourceSet The resource set the resource reference |
516
|
|
|
* property points to |
517
|
|
|
*/ |
518
|
|
|
public function addResourceReferenceProperty($resourceType, $name, $targetResourceSet) |
519
|
|
|
{ |
520
|
|
|
$this->addReferencePropertyInternal( |
521
|
|
|
$resourceType, |
522
|
|
|
$name, |
523
|
|
|
$targetResourceSet, |
524
|
|
|
'0..1' |
525
|
|
|
); |
526
|
|
|
} |
527
|
|
|
|
528
|
|
|
/** |
529
|
|
|
* To add a 1:N resource reference property. |
530
|
|
|
* |
531
|
|
|
* @param ResourceType $sourceResourceType The resource type to add the resource |
|
|
|
|
532
|
|
|
* reference property from |
533
|
|
|
* @param ResourceType $targetResourceType The resource type to add the resource |
|
|
|
|
534
|
|
|
* reference property to |
535
|
|
|
* @param string $sourceProperty The name of the property to add, on source type |
536
|
|
|
* @param string $targetProperty The name of the property to add, on target type |
537
|
|
|
*/ |
538
|
|
View Code Duplication |
public function addResourceReferencePropertyBidirectional( |
|
|
|
|
539
|
|
|
ResourceEntityType $sourceResourceType, |
540
|
|
|
ResourceEntityType $targetResourceType, |
541
|
|
|
$sourceProperty, |
542
|
|
|
$targetProperty |
543
|
|
|
) { |
544
|
|
|
$this->addReferencePropertyInternalBidirectional( |
545
|
|
|
$sourceResourceType, |
546
|
|
|
$targetResourceType, |
547
|
|
|
$sourceProperty, |
548
|
|
|
$targetProperty, |
549
|
|
|
'*', |
550
|
|
|
'1' |
551
|
|
|
); |
552
|
|
|
// verify resource property types are what we expect them to be |
553
|
|
|
$sourceResourceKind = $sourceResourceType->resolveProperty($sourceProperty)->getKind(); |
554
|
|
|
assert( |
555
|
|
|
ResourcePropertyKind::RESOURCE_REFERENCE == $sourceResourceKind, |
556
|
|
|
"1 side of 1:N relationship not pointing to resource reference" |
|
|
|
|
557
|
|
|
); |
558
|
|
|
$targetResourceKind = $targetResourceType->resolveProperty($targetProperty)->getKind(); |
559
|
|
|
assert( |
560
|
|
|
ResourcePropertyKind::RESOURCESET_REFERENCE == $targetResourceKind, |
561
|
|
|
"N side of 1:N relationship not pointing to resource set reference" |
|
|
|
|
562
|
|
|
); |
563
|
|
|
} |
564
|
|
|
|
565
|
|
|
/** |
566
|
|
|
* To add a navigation property (resource set or resource reference) |
567
|
|
|
* to a resource type. |
568
|
|
|
* |
569
|
|
|
* @param ResourceEntityType $sourceResourceType The resource type to add |
570
|
|
|
* the resource reference |
571
|
|
|
* or resource |
572
|
|
|
* reference set property to |
573
|
|
|
* @param string $name The name of the |
574
|
|
|
* property to add |
575
|
|
|
* @param ResourceSet $targetResourceSet The resource set the |
576
|
|
|
* resource reference |
577
|
|
|
* or reference |
578
|
|
|
* set property |
579
|
|
|
* points to |
580
|
|
|
* @param string $resourceMult The multiplicity of relation being added |
581
|
|
|
*/ |
582
|
|
|
private function addReferencePropertyInternal( |
583
|
|
|
ResourceEntityType $sourceResourceType, |
584
|
|
|
$name, |
585
|
|
|
ResourceSet $targetResourceSet, |
586
|
|
|
$resourceMult |
587
|
|
|
) { |
588
|
|
|
$allowedMult = ['*', '1', '0..1']; |
589
|
|
|
$this->checkInstanceProperty($name, $sourceResourceType); |
590
|
|
|
|
591
|
|
|
// check that property and resource name don't up and collide - would violate OData spec |
592
|
|
|
if (strtolower($name) == strtolower($sourceResourceType->getName())) { |
593
|
|
|
throw new InvalidOperationException( |
594
|
|
|
'Property name must be different from resource name.' |
595
|
|
|
); |
596
|
|
|
} |
597
|
|
|
if (!in_array($resourceMult, $allowedMult)) { |
598
|
|
|
throw new InvalidOperationException("Supplied multiplicity ".$resourceMult." not valid"); |
|
|
|
|
599
|
|
|
} |
600
|
|
|
|
601
|
|
|
$resourcePropertyKind = ('*' == $resourceMult) |
602
|
|
|
? ResourcePropertyKind::RESOURCESET_REFERENCE |
603
|
|
|
: ResourcePropertyKind::RESOURCE_REFERENCE; |
604
|
|
|
$targetResourceType = $targetResourceSet->getResourceType(); |
605
|
|
|
$sourceResourceProperty = new ResourceProperty($name, null, $resourcePropertyKind, $targetResourceType); |
|
|
|
|
606
|
|
|
$sourceResourceType->addProperty($sourceResourceProperty); |
607
|
|
|
|
608
|
|
|
//Create instance of AssociationSet for this relationship |
609
|
|
|
$sourceResourceSet = $sourceResourceType->getCustomState(); |
610
|
|
|
if (!$sourceResourceSet instanceof ResourceSet) { |
611
|
|
|
throw new InvalidOperationException( |
612
|
|
|
'Failed to retrieve the custom state from ' |
613
|
|
|
. $sourceResourceType->getName() |
614
|
|
|
); |
615
|
|
|
} |
616
|
|
|
|
617
|
|
|
//Customer_Orders_Orders, Order_Customer_Customers |
618
|
|
|
//(source type::name _ source property::name _ target set::name) |
619
|
|
|
$setKey = ResourceAssociationSet::keyName($sourceResourceType, $name, $targetResourceSet); |
620
|
|
|
//$setKey = $sourceResourceType->getName() . '_' . $name . '_' . $targetResourceType->getName(); |
|
|
|
|
621
|
|
|
$set = new ResourceAssociationSet( |
622
|
|
|
$setKey, |
623
|
|
|
new ResourceAssociationSetEnd($sourceResourceSet, $sourceResourceType, $sourceResourceProperty), |
624
|
|
|
new ResourceAssociationSetEnd($targetResourceSet, $targetResourceType, null) |
625
|
|
|
); |
626
|
|
|
$mult = $resourceMult; |
627
|
|
|
$backMult = '*' == $resourceMult ? '*' : '1'; |
628
|
|
|
$this->metadataManager->addNavigationPropertyToEntityType( |
629
|
|
|
$this->OdataEntityMap[$sourceResourceType->getFullName()], |
630
|
|
|
$mult, |
631
|
|
|
$name, |
632
|
|
|
$this->OdataEntityMap[$targetResourceType->getFullName()], |
633
|
|
|
$backMult |
634
|
|
|
); |
635
|
|
|
$this->associationSets[$setKey] = $set; |
636
|
|
|
} |
637
|
|
|
|
638
|
|
|
/** |
639
|
|
|
* To add a navigation property (resource set or resource reference) |
640
|
|
|
* to a resource type. |
641
|
|
|
* |
642
|
|
|
* @param ResourceEntityType $sourceResourceType The source resource type to add |
643
|
|
|
* the resource reference |
644
|
|
|
* or resource reference set property to |
645
|
|
|
* @param ResourceEntityType $targetResourceType The target resource type to add |
646
|
|
|
* the resource reference |
647
|
|
|
* or resource reference set property to |
648
|
|
|
* @param string $sourceProperty The name of the |
649
|
|
|
* property to add to source type |
650
|
|
|
* @param string $targetProperty The name of the |
651
|
|
|
* property to add to target type |
652
|
|
|
* @param string $sourceMultiplicity The multiplicity at the source end of relation |
653
|
|
|
* @param string $targetMultiplicity The multiplicity at the target end of relation |
654
|
|
|
*/ |
655
|
|
|
private function addReferencePropertyInternalBidirectional( |
656
|
|
|
ResourceEntityType $sourceResourceType, |
657
|
|
|
ResourceEntityType $targetResourceType, |
658
|
|
|
$sourceProperty, |
659
|
|
|
$targetProperty, |
660
|
|
|
$sourceMultiplicity, |
661
|
|
|
$targetMultiplicity |
662
|
|
|
) { |
663
|
|
|
if (!is_string($sourceProperty) || !is_string($targetProperty)) { |
664
|
|
|
throw new InvalidOperationException("Source and target properties must both be strings"); |
|
|
|
|
665
|
|
|
} |
666
|
|
|
|
667
|
|
|
$this->checkInstanceProperty($sourceProperty, $sourceResourceType); |
668
|
|
|
$this->checkInstanceProperty($targetProperty, $targetResourceType); |
669
|
|
|
|
670
|
|
|
// check that property and resource name don't up and collide - would violate OData spec |
671
|
|
|
if (strtolower($sourceProperty) == strtolower($sourceResourceType->getName())) { |
672
|
|
|
throw new InvalidOperationException( |
673
|
|
|
'Source property name must be different from source resource name.' |
674
|
|
|
); |
675
|
|
|
} |
676
|
|
|
if (strtolower($targetProperty) == strtolower($targetResourceType->getName())) { |
677
|
|
|
throw new InvalidOperationException( |
678
|
|
|
'Target property name must be different from target resource name.' |
679
|
|
|
); |
680
|
|
|
} |
681
|
|
|
|
682
|
|
|
//Create instance of AssociationSet for this relationship |
683
|
|
|
$sourceResourceSet = $sourceResourceType->getCustomState(); |
684
|
|
|
if (!$sourceResourceSet instanceof ResourceSet) { |
685
|
|
|
throw new InvalidOperationException( |
686
|
|
|
'Failed to retrieve the custom state from ' |
687
|
|
|
. $sourceResourceType->getName() |
688
|
|
|
); |
689
|
|
|
} |
690
|
|
|
$targetResourceSet = $targetResourceType->getCustomState(); |
691
|
|
|
if (!$targetResourceSet instanceof ResourceSet) { |
692
|
|
|
throw new InvalidOperationException( |
693
|
|
|
'Failed to retrieve the custom state from ' |
694
|
|
|
. $targetResourceType->getName() |
695
|
|
|
); |
696
|
|
|
} |
697
|
|
|
|
698
|
|
|
//Customer_Orders_Orders, Order_Customer_Customers |
699
|
|
|
$fwdSetKey = ResourceAssociationSet::keyName($sourceResourceType, $sourceProperty, $targetResourceSet); |
700
|
|
|
$revSetKey = ResourceAssociationSet::keyName($targetResourceType, $targetProperty, $sourceResourceSet); |
701
|
|
|
if (isset($this->associationSets[$fwdSetKey]) && $this->associationSets[$revSetKey]) { |
702
|
|
|
return; |
703
|
|
|
} |
704
|
|
|
$sourceKind = ('*' == $sourceMultiplicity) |
705
|
|
|
? ResourcePropertyKind::RESOURCESET_REFERENCE |
706
|
|
|
: ResourcePropertyKind::RESOURCE_REFERENCE; |
707
|
|
|
$targetKind = ('*' == $targetMultiplicity) |
708
|
|
|
? ResourcePropertyKind::RESOURCESET_REFERENCE |
709
|
|
|
: ResourcePropertyKind::RESOURCE_REFERENCE; |
710
|
|
|
|
711
|
|
|
$sourceResourceProperty = new ResourceProperty($sourceProperty, null, $targetKind, $targetResourceType); |
|
|
|
|
712
|
|
|
assert( |
713
|
|
|
$targetKind == $sourceResourceProperty->getKind(), |
714
|
|
|
'Resource property kind mismatch between $targetKind and $sourceResourceProperty' |
715
|
|
|
); |
716
|
|
|
$sourceResourceType->addProperty($sourceResourceProperty, false); |
717
|
|
|
$targetResourceProperty = new ResourceProperty($targetProperty, null, $sourceKind, $sourceResourceType); |
|
|
|
|
718
|
|
|
assert( |
719
|
|
|
$sourceKind == $targetResourceProperty->getKind(), |
720
|
|
|
'Resource property kind mismatch between $sourceKind and $targetResourceProperty' |
721
|
|
|
); |
722
|
|
|
$targetResourceType->addProperty($targetResourceProperty, false); |
723
|
|
|
|
724
|
|
|
//TODO: Audit this, figure out how it makes metadata go sproing |
|
|
|
|
725
|
|
|
$fwdSet = new ResourceAssociationSet( |
726
|
|
|
$fwdSetKey, |
727
|
|
|
new ResourceAssociationSetEnd($sourceResourceSet, $sourceResourceType, $sourceResourceProperty), |
728
|
|
|
new ResourceAssociationSetEnd($targetResourceSet, $targetResourceType, $targetResourceProperty) |
729
|
|
|
); |
730
|
|
|
$revSet = new ResourceAssociationSet( |
731
|
|
|
$revSetKey, |
732
|
|
|
new ResourceAssociationSetEnd($targetResourceSet, $targetResourceType, $targetResourceProperty), |
733
|
|
|
new ResourceAssociationSetEnd($sourceResourceSet, $sourceResourceType, $sourceResourceProperty) |
734
|
|
|
); |
735
|
|
|
$sourceName = $sourceResourceType->getFullName(); |
736
|
|
|
$targetName = $targetResourceType->getFullName(); |
737
|
|
|
$this->metadataManager->addNavigationPropertyToEntityType( |
738
|
|
|
$this->OdataEntityMap[$sourceName], |
739
|
|
|
$sourceMultiplicity, |
740
|
|
|
$sourceProperty, |
741
|
|
|
$this->OdataEntityMap[$targetName], |
742
|
|
|
$targetMultiplicity, |
743
|
|
|
$targetProperty |
744
|
|
|
); |
745
|
|
|
$this->associationSets[$fwdSetKey] = $fwdSet; |
746
|
|
|
$this->associationSets[$revSetKey] = $revSet; |
747
|
|
|
} |
748
|
|
|
|
749
|
|
|
/** |
750
|
|
|
* To add a resource set reference property. |
751
|
|
|
* |
752
|
|
|
* @param ResourceEntityType $resourceType The resource type to add the |
753
|
|
|
* resource reference set property to |
754
|
|
|
* @param string $name The name of the property to add |
755
|
|
|
* @param ResourceSet $targetResourceSet The resource set the resource |
756
|
|
|
* reference set property points to |
757
|
|
|
*/ |
758
|
|
|
public function addResourceSetReferenceProperty(ResourceEntityType $resourceType, $name, $targetResourceSet) |
759
|
|
|
{ |
760
|
|
|
$this->addReferencePropertyInternal( |
761
|
|
|
$resourceType, |
762
|
|
|
$name, |
763
|
|
|
$targetResourceSet, |
764
|
|
|
'*' |
765
|
|
|
); |
766
|
|
|
} |
767
|
|
|
|
768
|
|
|
/** |
769
|
|
|
* To add a M:N resource reference property. |
770
|
|
|
* |
771
|
|
|
* @param ResourceEntityType $sourceResourceType The resource type to add the resource |
772
|
|
|
* reference property from |
773
|
|
|
* @param ResourceEntityType $targetResourceType The resource type to add the resource |
774
|
|
|
* reference property to |
775
|
|
|
* @param string $sourceProperty The name of the property to add, on source type |
776
|
|
|
* @param string $targetProperty The name of the property to add, on target type |
777
|
|
|
*/ |
778
|
|
View Code Duplication |
public function addResourceSetReferencePropertyBidirectional( |
|
|
|
|
779
|
|
|
ResourceEntityType $sourceResourceType, |
780
|
|
|
ResourceEntityType $targetResourceType, |
781
|
|
|
$sourceProperty, |
782
|
|
|
$targetProperty |
783
|
|
|
) { |
784
|
|
|
$this->addReferencePropertyInternalBidirectional( |
785
|
|
|
$sourceResourceType, |
786
|
|
|
$targetResourceType, |
787
|
|
|
$sourceProperty, |
788
|
|
|
$targetProperty, |
789
|
|
|
'*', |
790
|
|
|
'*' |
791
|
|
|
); |
792
|
|
|
// verify resource property types are what we expect them to be |
793
|
|
|
$sourceResourceKind = $sourceResourceType->resolveProperty($sourceProperty)->getKind(); |
794
|
|
|
assert( |
795
|
|
|
ResourcePropertyKind::RESOURCESET_REFERENCE == $sourceResourceKind, |
796
|
|
|
"M side of M:N relationship not pointing to resource set reference" |
|
|
|
|
797
|
|
|
); |
798
|
|
|
$targetResourceKind = $targetResourceType->resolveProperty($targetProperty)->getKind(); |
799
|
|
|
assert( |
800
|
|
|
ResourcePropertyKind::RESOURCESET_REFERENCE == $targetResourceKind, |
801
|
|
|
"N side of M:N relationship not pointing to resource set reference" |
|
|
|
|
802
|
|
|
); |
803
|
|
|
} |
804
|
|
|
|
805
|
|
|
/** |
806
|
|
|
* To add a 1-1 resource reference. |
807
|
|
|
* |
808
|
|
|
* @param ResourceEntityType $sourceResourceType The resource type to add the resource |
809
|
|
|
* reference property from |
810
|
|
|
* @param ResourceEntityType $targetResourceType The resource type to add the resource |
811
|
|
|
* reference property to |
812
|
|
|
* @param string $sourceProperty The name of the property to add, on source type |
813
|
|
|
* @param string $targetProperty The name of the property to add, on target type |
814
|
|
|
*/ |
815
|
|
View Code Duplication |
public function addResourceReferenceSinglePropertyBidirectional( |
|
|
|
|
816
|
|
|
ResourceEntityType $sourceResourceType, |
817
|
|
|
ResourceEntityType $targetResourceType, |
818
|
|
|
$sourceProperty, |
819
|
|
|
$targetProperty |
820
|
|
|
) { |
821
|
|
|
$this->addReferencePropertyInternalBidirectional( |
822
|
|
|
$sourceResourceType, |
823
|
|
|
$targetResourceType, |
824
|
|
|
$sourceProperty, |
825
|
|
|
$targetProperty, |
826
|
|
|
'1', |
827
|
|
|
'0..1' |
828
|
|
|
); |
829
|
|
|
// verify resource property types are what we expect them to be |
830
|
|
|
$sourceResourceKind = $sourceResourceType->resolveProperty($sourceProperty)->getKind(); |
831
|
|
|
assert( |
832
|
|
|
ResourcePropertyKind::RESOURCE_REFERENCE == $sourceResourceKind, |
833
|
|
|
"1 side of 1:1 relationship not pointing to resource reference" |
|
|
|
|
834
|
|
|
); |
835
|
|
|
$targetResourceKind = $targetResourceType->resolveProperty($targetProperty)->getKind(); |
836
|
|
|
assert( |
837
|
|
|
ResourcePropertyKind::RESOURCE_REFERENCE == $targetResourceKind, |
838
|
|
|
"0..1 side of 1:1 relationship not pointing to resource reference" |
|
|
|
|
839
|
|
|
); |
840
|
|
|
} |
841
|
|
|
|
842
|
|
|
/** |
843
|
|
|
* To add a complex property to entity or complex type. |
844
|
|
|
* |
845
|
|
|
* @param ResourceType $targetResourceType The resource type to which the complex property needs to add |
846
|
|
|
* @param string $name name of the complex property |
847
|
|
|
* @param ResourceComplexType $complexResourceType complex resource type |
848
|
|
|
* @param bool $isBag complex type is bag or not |
849
|
|
|
* |
850
|
|
|
* @return ResourceProperty |
851
|
|
|
*/ |
852
|
|
|
public function addComplexProperty( |
853
|
|
|
ResourceType $targetResourceType, |
854
|
|
|
$name, |
855
|
|
|
ResourceComplexType $complexResourceType, |
856
|
|
|
$isBag = false |
857
|
|
|
) { |
858
|
|
|
if ($targetResourceType->getResourceTypeKind() != ResourceTypeKind::ENTITY |
859
|
|
|
&& $targetResourceType->getResourceTypeKind() != ResourceTypeKind::COMPLEX |
860
|
|
|
) { |
861
|
|
|
throw new InvalidOperationException('Complex property can be added to an entity or another complex type'); |
862
|
|
|
} |
863
|
|
|
|
864
|
|
|
// check that property and resource name don't up and collide - would violate OData spec |
865
|
|
|
if (strtolower($name) == strtolower($targetResourceType->getName())) { |
866
|
|
|
throw new InvalidOperationException( |
867
|
|
|
'Property name must be different from resource name.' |
868
|
|
|
); |
869
|
|
|
} |
870
|
|
|
|
871
|
|
|
$this->checkInstanceProperty($name, $targetResourceType); |
872
|
|
|
|
873
|
|
|
$kind = ResourcePropertyKind::COMPLEX_TYPE; |
874
|
|
|
if ($isBag) { |
875
|
|
|
$kind = $kind | ResourcePropertyKind::BAG; |
876
|
|
|
} |
877
|
|
|
|
878
|
|
|
$resourceProperty = new ResourceProperty($name, null, $kind, $complexResourceType); |
879
|
|
|
$targetResourceType->addProperty($resourceProperty); |
880
|
|
|
|
881
|
|
|
return $resourceProperty; |
882
|
|
|
} |
883
|
|
|
|
884
|
|
|
public function createSingleton($name, ResourceType $returnType, $functionName) |
885
|
|
|
{ |
886
|
|
|
$msg = null; |
887
|
|
|
if (array_key_exists($name, $this->singletons)) { |
888
|
|
|
$msg = "Singleton name already exists"; |
|
|
|
|
889
|
|
|
throw new \InvalidArgumentException($msg); |
890
|
|
|
} |
891
|
|
|
if (array_key_exists($name, $this->resourceSets)) { |
892
|
|
|
$msg = "Resource set with same name, ". $name. ", exists"; |
|
|
|
|
893
|
|
|
throw new \InvalidArgumentException($msg); |
894
|
|
|
} |
895
|
|
|
$typeName = $returnType->getName(); |
896
|
|
|
if (!array_key_exists($typeName, $this->OdataEntityMap)) { |
897
|
|
|
$msg = "Mapping not defined for ".$typeName; |
|
|
|
|
898
|
|
|
throw new \InvalidArgumentException($msg); |
899
|
|
|
} |
900
|
|
|
$metaReturn = $this->OdataEntityMap[$typeName]; |
901
|
|
|
$singleton = $this->metadataManager->createSingleton($name, $metaReturn); |
902
|
|
|
assert($singleton->isOK($msg), $msg); |
903
|
|
|
$type = new ResourceFunctionType($functionName, $singleton, $returnType); |
904
|
|
|
// Since singletons should take no args, enforce it here |
905
|
|
|
assert(0 == count($type->getParms())); |
906
|
|
|
$this->singletons[$name] = $type; |
907
|
|
|
} |
908
|
|
|
|
909
|
|
|
public function getSingletons() |
910
|
|
|
{ |
911
|
|
|
return $this->singletons; |
912
|
|
|
} |
913
|
|
|
|
914
|
|
|
public function callSingleton($name) |
915
|
|
|
{ |
916
|
|
|
if (!array_key_exists($name, $this->singletons)) { |
917
|
|
|
$msg = "Requested singleton does not exist"; |
|
|
|
|
918
|
|
|
throw new \InvalidArgumentException($msg); |
919
|
|
|
} |
920
|
|
|
|
921
|
|
|
return $this->singletons[$name]->get(); |
922
|
|
|
} |
923
|
|
|
} |
924
|
|
|
|
This check examines a number of code elements and verifies that they conform to the given naming conventions.
You can set conventions for local variables, abstract classes, utility classes, constant, properties, methods, parameters, interfaces, classes, exceptions and special methods.