Test Setup Failed
Push — master ( a79f89...c04478 )
by Alex
04:10
created

SimpleMetadataProvider::getAssociationCount()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 4
rs 10
cc 1
eloc 2
nc 1
nop 0
1
<?php
2
3
namespace POData\Providers\Metadata;
4
5
use AlgoWeb\ODataMetadata\MetadataManager;
6
use POData\Common\InvalidOperationException;
7
use POData\Providers\Metadata\Type\IType;
8
use POData\Providers\Metadata\Type\TypeCode;
9
10
/**
11
 * Class SimpleMetadataProvider.
12
 */
13
class SimpleMetadataProvider implements IMetadataProvider
14
{
15
    public $OdataEntityMap = [];
16
    protected $resourceSets = [];
17
    protected $resourceTypes = [];
18
    protected $associationSets = [];
19
    protected $containerName;
20
    protected $namespaceName;
21
    private $metadataManager;
22
23
    /**
24
     * @param string $containerName container name for the datasource
25
     * @param string $namespaceName namespace for the datasource
26
     */
27
    public function __construct($containerName, $namespaceName)
28
    {
29
        $this->containerName = $containerName;
30
        $this->namespaceName = $namespaceName;
31
        $this->metadataManager = new MetadataManager($namespaceName, $containerName);
32
    }
33
34
    //Begin Implementation of IMetadataProvider
35
36
    public function getXML()
37
    {
38
        return $this->metadataManager->getEdmxXML();
39
    }
40
41
    /**
42
     * get the Container name for the data source.
43
     *
44
     * @return string container name
45
     */
46
    public function getContainerName()
47
    {
48
        return $this->containerName;
49
    }
50
51
    /**
52
     * get Namespace name for the data source.
53
     *
54
     * @return string namespace
55
     */
56
    public function getContainerNamespace()
57
    {
58
        return $this->namespaceName;
59
    }
60
61
    /**
62
     * get all entity set information.
63
     *
64
     * @return ResourceSet[]
65
     */
66
    public function getResourceSets($params = null)
67
    {
68
        $parameters = [];
69
        if (is_string($params)) {
70
            $parameters[] = $params;
71
        } elseif (isset($params) && !is_array($params)) {
72
            throw new \ErrorException('Input parameter must be absent, null, string or array');
73
        } else {
74
            $parameters = $params;
75
        }
76
        if (!is_array($parameters) || 0 == count($parameters)) {
77
            return array_values($this->resourceSets);
78
        }
79
        assert(is_array($parameters));
80
        $return = [];
81
        $counter = 0;
82
        foreach ($this->resourceSets as $resource) {
83
            $resName = $resource->getName();
84
            if (in_array($resName, $parameters)) {
85
                $return[] = $resource;
86
                $counter++;
87
            }
88
        }
89
        assert($counter == count($return));
90
91
        return $return;
92
    }
93
94
    /**
95
     * get all resource types in the data source.
96
     *
97
     * @return ResourceType[]
98
     */
99
    public function getTypes()
100
    {
101
        return array_values($this->resourceTypes);
102
    }
103
104
    /**
105
     * get a resource set based on the specified resource set name.
106
     *
107
     * @param string $name Name of the resource set
108
     *
109
     * @return ResourceSet|null resource set with the given name if found else NULL
110
     */
111
    public function resolveResourceSet($name)
112
    {
113
        if (array_key_exists($name, $this->resourceSets)) {
114
            return $this->resourceSets[$name];
115
        }
116
        return null;
117
    }
118
119
    /**
120
     * get a resource type based on the resource type name.
121
     *
122
     * @param string $name Name of the resource type
123
     *
124
     * @return ResourceType|null resource type with the given resource type name if found else NULL
125
     */
126
    public function resolveResourceType($name)
127
    {
128
        if (array_key_exists($name, $this->resourceTypes)) {
129
            return $this->resourceTypes[$name];
130
        }
131
        return null;
132
    }
133
134
    /**
135
     * get a resource set based on the specified resource association set name.
136
     *
137
     * @param string $name Name of the resource assocation set
138
     *
139
     * @return ResourceAssociationSet|null resource association set with the given name if found else NULL
140
     */
141
    public function resolveAssociationSet($name)
142
    {
143
        if (array_key_exists($name, $this->associationSets)) {
144
            return $this->associationSets[$name];
145
        }
146
        return null;
147
    }
148
149
    /*
150
     * Get number of association sets hooked up
151
     */
152
    public function getAssociationCount()
153
    {
154
        return count($this->associationSets);
155
    }
156
157
    /**
158
     * The method must return a collection of all the types derived from
159
     * $resourceType The collection returned should NOT include the type
160
     * passed in as a parameter.
161
     *
162
     * @param ResourceType $resourceType Resource to get derived resource types from
163
     *
164
     * @return ResourceType[]
165
     */
166
    public function getDerivedTypes(ResourceType $resourceType)
167
    {
168
        return [];
169
    }
170
171
    /**
172
     * @param ResourceType $resourceType Resource to check for derived resource types
173
     *
174
     * @return bool true if $resourceType represents an Entity Type which has derived Entity Types, else false
175
     */
176
    public function hasDerivedTypes(ResourceType $resourceType)
177
    {
178
        return false;
179
    }
180
181
    //End Implementation of IMetadataProvider
182
183
    /**
184
     * Gets the ResourceAssociationSet instance for the given source
185
     * association end.
186
     *
187
     * @param ResourceSet      $sourceResourceSet      Resource set
188
     *                                                 of the source
189
     *                                                 association end
190
     * @param ResourceType     $sourceResourceType     Resource type of the source
191
     *                                                 association end
192
     * @param ResourceProperty $targetResourceProperty Resource property of
193
     *                                                 the source
194
     *                                                 association end
195
     *
196
     * @return ResourceAssociationSet|null
197
     */
198
    public function getResourceAssociationSet(
199
        ResourceSet $sourceResourceSet,
200
        ResourceType $sourceResourceType,
201
        ResourceProperty $targetResourceProperty
202
    ) {
203
        //e.g.
204
        //ResourceSet => Representing 'Customers' entity set
205
        //ResourceType => Representing'Customer' entity type
206
        //ResourceProperty => Representing 'Orders' property
207
        //We have created ResourceAssoicationSet while adding
208
        //ResourceSetReference or ResourceReference
209
        //and kept in $this->associationSets
210
        //$metadata->addResourceSetReferenceProperty(
211
        //             $customersEntityType,
212
        //             'Orders',
213
        //             $ordersResourceSet
214
        //             );
215
216
        $targetResourceSet = $targetResourceProperty->getResourceType()->getCustomState();
217
        if (is_null($targetResourceSet)) {
218
            throw new InvalidOperationException(
219
                'Failed to retrieve the custom state from ' . $targetResourceProperty->getResourceType()->getName()
220
            );
221
        }
222
223
        //Customer_Orders_Orders, Order_Customer_Customers
224
        $key = ResourceAssociationSet::keyName(
225
            $sourceResourceType,
226
            $targetResourceProperty->getName(),
227
            $targetResourceSet
228
        );
229
230
        $associationSet = array_key_exists($key, $this->associationSets) ? $this->associationSets[$key] : null;
231
        assert(
232
            null == $associationSet || $associationSet instanceof ResourceAssociationSet,
233
            "Retrieved resource assocation must be either null or an instance of ResourceAssociationSet"
234
        );
235
        return $associationSet;
236
    }
237
238
    /**
239
     * Add an entity type.
240
     *
241
     * @param \ReflectionClass $refClass  reflection class of the entity
242
     * @param string           $name      name of the entity
243
     * @param string           $namespace namespace of the data source
244
     *
245
     * @throws InvalidOperationException when the name is already in use
246
     *
247
     * @return ResourceType
248
     */
249
    public function addEntityType(\ReflectionClass $refClass, $name, $namespace = null)
250
    {
251
        return $this->createResourceType($refClass, $name, $namespace, ResourceTypeKind::ENTITY, null);
252
    }
253
254
    /**
255
     * @param \ReflectionClass $refClass
256
     * @param string $name
257
     * @param string|null $namespace
258
     * @param $typeKind
259
     * @param null|ResourceType $baseResourceType
260
     *
261
     * @throws InvalidOperationException
262
     *
263
     * @return ResourceType
264
     */
265
    private function createResourceType(
266
        \ReflectionClass $refClass,
267
        $name,
268
        $namespace,
269
        $typeKind,
270
        $baseResourceType
271
    ) {
272
        if (array_key_exists($name, $this->resourceTypes)) {
273
            throw new InvalidOperationException('Type with same name already added');
274
        }
275
276
        $entityType = new ResourceType($refClass, $typeKind, $name, $namespace, $baseResourceType);
277
        $this->resourceTypes[$name] = $entityType;
278
        ksort($this->resourceTypes);
279
280
        if ($typeKind == ResourceTypeKind::ENTITY) {
281
            $this->OdataEntityMap[$entityType->getFullName()] = $this->metadataManager->addEntityType($name);
282
        }
283
284
285
        return $entityType;
286
    }
287
288
    /**
289
     * Add a complex type.
290
     *
291
     * @param \ReflectionClass $refClass         reflection class of the complex entity type
292
     * @param string           $name             name of the entity
293
     * @param string           $namespace        namespace of the data source
294
     * @param ResourceType     $baseResourceType base resource type
295
     *
296
     * @throws InvalidOperationException when the name is already in use
297
     *
298
     * @return ResourceType
299
     */
300
    public function addComplexType(\ReflectionClass $refClass, $name, $namespace = null, $baseResourceType = null)
301
    {
302
        return $this->createResourceType($refClass, $name, $namespace, ResourceTypeKind::COMPLEX, $baseResourceType);
303
    }
304
305
    /**
306
     * @param string       $name         name of the resource set
307
     * @param ResourceType $resourceType resource type
308
     *
309
     * @throws InvalidOperationException
310
     *
311
     * @return ResourceSet
312
     */
313
    public function addResourceSet($name, ResourceType $resourceType)
314
    {
315
        if (array_key_exists($name, $this->resourceSets)) {
316
            throw new InvalidOperationException('Resource Set already added');
317
        }
318
319
        $this->resourceSets[$name] = new ResourceSet($name, $resourceType);
320
        //No support for multiple ResourceSet with same EntityType
321
        //So keeping reference to the 'ResourceSet' with the entity type
322
        $resourceType->setCustomState($this->resourceSets[$name]);
323
        ksort($this->resourceSets);
324
325
        return $this->resourceSets[$name];
326
    }
327
328
    /**
329
     * To add a Key-primitive property to a resource (Complex/Entity).
330
     *
331
     * @param ResourceType $resourceType resource type to which key property
332
     *                                   is to be added
333
     * @param string       $name         name of the key property
334
     * @param TypeCode     $typeCode     type of the key property
335
     */
336
    public function addKeyProperty($resourceType, $name, $typeCode)
337
    {
338
        $this->_addPrimitivePropertyInternal($resourceType, $name, $typeCode, true);
339
    }
340
341
    /**
342
     * To add a Key/NonKey-primitive property to a resource (complex/entity).
343
     *
344
     * @param ResourceType $resourceType   Resource type
345
     * @param string       $name           name of the property
346
     * @param TypeCode     $typeCode       type of property
347
     * @param bool         $isKey          property is key or not
348
     * @param bool         $isBag          property is bag or not
349
     * @param bool         $isETagProperty property is etag or not
350
     */
351
    private function _addPrimitivePropertyInternal(
352
        $resourceType,
353
        $name,
354
        $typeCode,
355
        $isKey = false,
356
        $isBag = false,
357
        $isETagProperty = false
358
    ) {
359
        $this->checkInstanceProperty($name, $resourceType);
360
361
        // check that property and resource name don't up and collide - would violate OData spec
362
        if (strtolower($name) == strtolower($resourceType->getName())) {
363
            throw new InvalidOperationException(
364
                'Property name must be different from resource name.'
365
            );
366
        }
367
368
        $primitiveResourceType = ResourceType::getPrimitiveResourceType($typeCode);
0 ignored issues
show
Documentation introduced by
$typeCode is of type object<POData\Providers\Metadata\Type\TypeCode>, but the function expects a object<POData\Providers\...\Type\EdmPrimitiveType>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
369
370
        if ($isETagProperty && $isBag) {
371
            throw new InvalidOperationException(
372
                'Only primitve property can be etag property, bag property cannot be etag property.'
373
            );
374
        }
375
376
        $kind = $isKey ? ResourcePropertyKind::PRIMITIVE | ResourcePropertyKind::KEY : ResourcePropertyKind::PRIMITIVE;
377
        if ($isBag) {
378
            $kind = $kind | ResourcePropertyKind::BAG;
379
        }
380
381
        if ($isETagProperty) {
382
            $kind = $kind | ResourcePropertyKind::ETAG;
383
        }
384
385
        $resourceProperty = new ResourceProperty($name, null, $kind, $primitiveResourceType);
386
        $resourceType->addProperty($resourceProperty);
387
        if (array_key_exists($resourceType->getFullName(), $this->OdataEntityMap)) {
388
            $this->metadataManager->addPropertyToEntityType(
389
                $this->OdataEntityMap[$resourceType->getFullName()],
390
                $name,
391
                $primitiveResourceType->getFullName(),
392
                null,
393
                false,
394
                $isKey
395
            );
396
        }
397
    }
398
399
    /**
400
     * @param string $name
401
     * @param ResourceType $resourceType
402
     *
403
     * @throws InvalidOperationException
404
     */
405
    private function checkInstanceProperty($name, ResourceType $resourceType)
406
    {
407
        $instance = $resourceType->getInstanceType();
408
        $hasMagicGetter = $instance instanceof IType || $instance->hasMethod('__get');
409
410
        if (!$hasMagicGetter) {
411
            try {
412
                if ($instance instanceof \ReflectionClass) {
413
                    $instance->getProperty($name);
414
                }
415
            } catch (\ReflectionException $exception) {
416
                throw new InvalidOperationException(
417
                    'Can\'t add a property which does not exist on the instance type.'
418
                );
419
            }
420
        }
421
    }
422
423
    /**
424
     * To add a NonKey-primitive property (Complex/Entity).
425
     *
426
     * @param ResourceType $resourceType resource type to which key property
427
     *                                   is to be added
428
     * @param string $name name of the key property
429
     * @param TypeCode $typeCode type of the key property
430
     * @param bool $isBag property is bag or not
431
     */
432
    public function addPrimitiveProperty($resourceType, $name, $typeCode, $isBag = false)
433
    {
434
        $this->_addPrimitivePropertyInternal($resourceType, $name, $typeCode, false, $isBag);
435
    }
436
437
    /**
438
     * To add a non-key etag property.
439
     *
440
     * @param ResourceType $resourceType resource type to which key property
441
     *                                   is to be added
442
     * @param string $name name of the property
443
     * @param TypeCode $typeCode type of the etag property
444
     */
445
    public function addETagProperty($resourceType, $name, $typeCode)
446
    {
447
        $this->_addPrimitivePropertyInternal($resourceType, $name, $typeCode, false, false, true);
448
    }
449
450
    /**
451
     * To add a resource reference property.
452
     *
453
     * @param ResourceType $resourceType The resource type to add the resource
454
     *                                        reference property to
455
     * @param string $name The name of the property to add
456
     * @param ResourceSet $targetResourceSet The resource set the resource reference
457
     *                                        property points to
458
     */
459
    public function addResourceReferenceProperty($resourceType, $name, $targetResourceSet)
460
    {
461
        $this->_addReferencePropertyInternal(
462
            $resourceType,
463
            $name,
464
            $targetResourceSet,
465
            ResourcePropertyKind::RESOURCE_REFERENCE
466
        );
467
    }
468
469
    /**
470
     * To add a 1:N resource reference property.
471
     *
472
     * @param ResourceType $sourceResourceType  The resource type to add the resource
473
     *                                          reference property from
474
     * @param ResourceType $targetResourceType  The resource type to add the resource
475
     *                                          reference property to
476
     * @param string $sourceProperty            The name of the property to add, on source type
477
     * @param string $targetProperty            The name of the property to add, on target type
478
     */
479
    public function addResourceReferencePropertyBidirectional(
480
        ResourceType $sourceResourceType,
481
        ResourceType $targetResourceType,
482
        $sourceProperty,
483
        $targetProperty
484
    ) {
485
        $this->_addReferencePropertyInternalBidirectional(
486
            $sourceResourceType,
487
            $targetResourceType,
488
            $sourceProperty,
489
            $targetProperty,
490
            ResourcePropertyKind::RESOURCE_REFERENCE,
491
            ResourcePropertyKind::RESOURCESET_REFERENCE
492
        );
493
    }
494
495
    /**
496
     * To add a navigation property (resource set or resource reference)
497
     * to a resource type.
498
     *
499
     * @param ResourceType         $sourceResourceType   The resource type to add
500
     *                                                   the resource reference
501
     *                                                   or resource
502
     *                                                   reference set property to
503
     * @param string               $name                 The name of the
504
     *                                                   property to add
505
     * @param ResourceSet          $targetResourceSet    The resource set the
506
     *                                                   resource reference
507
     *                                                   or reference
508
     *                                                   set property
509
     *                                                   points to
510
     * @param ResourcePropertyKind $resourcePropertyKind The property kind
511
     */
512
    private function _addReferencePropertyInternal(
513
        ResourceType $sourceResourceType,
514
        $name,
515
        ResourceSet $targetResourceSet,
516
        $resourcePropertyKind
517
    ) {
518
        $this->checkInstanceProperty($name, $sourceResourceType);
519
520
        // check that property and resource name don't up and collide - would violate OData spec
521
        if (strtolower($name) == strtolower($sourceResourceType->getName())) {
522
            throw new InvalidOperationException(
523
                'Property name must be different from resource name.'
524
            );
525
        }
526
527
        $targetResourceType = $targetResourceSet->getResourceType();
528
        $sourceResourceProperty = new ResourceProperty($name, null, $resourcePropertyKind, $targetResourceType);
529
        $sourceResourceType->addProperty($sourceResourceProperty);
530
531
        //Create instance of AssociationSet for this relationship
532
        $sourceResourceSet = $sourceResourceType->getCustomState();
533
        if (!$sourceResourceSet instanceof ResourceSet) {
534
            throw new InvalidOperationException(
535
                'Failed to retrieve the custom state from '
536
                . $sourceResourceType->getName()
537
            );
538
        }
539
540
        //Customer_Orders_Orders, Order_Customer_Customers
541
        //(source type::name _ source property::name _ target set::name)
542
        $setKey = ResourceAssociationSet::keyName($sourceResourceType, $name, $targetResourceSet);
543
        //$setKey = $sourceResourceType->getName() . '_' . $name . '_' . $targetResourceType->getName();
0 ignored issues
show
Unused Code Comprehensibility introduced by
44% of this comment could be valid code. Did you maybe forget this after debugging?

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.

Loading history...
544
        $set = new ResourceAssociationSet(
545
            $setKey,
546
            new ResourceAssociationSetEnd($sourceResourceSet, $sourceResourceType, $sourceResourceProperty),
547
            new ResourceAssociationSetEnd($targetResourceSet, $targetResourceType, null)
548
        );
549
        $mult = $resourcePropertyKind == ResourcePropertyKind::RESOURCESET_REFERENCE ? "*" : "0..1";
550
        $this->metadataManager->addNavigationPropertyToEntityType(
551
            $this->OdataEntityMap[$sourceResourceType->getFullName()],
552
            $mult,
553
            $name,
554
            $this->OdataEntityMap[$targetResourceType->getFullName()],
555
            $mult
556
        );
557
        $this->associationSets[$setKey] = $set;
558
    }
559
560
    /**
561
     * To add a navigation property (resource set or resource reference)
562
     * to a resource type.
563
     *
564
     * @param ResourceType         $sourceResourceType   The source resource type to add
565
     *                                                   the resource reference
566
     *                                                   or resource reference set property to
567
     * @param ResourceType         $targetResourceType   The target resource type to add
568
     *                                                   the resource reference
569
     *                                                   or resource reference set property to
570
     * @param string               $sourceProperty       The name of the
571
     *                                                   property to add to source type
572
     * @param string               $targetProperty       The name of the
573
     *                                                   property to add to target type
574
     * @param ResourcePropertyKind $sourcePropertyKind   The property kind on the source type
575
     * @param ResourcePropertyKind $targetPropertyKind   The property kind on the target type
576
     */
577
    private function _addReferencePropertyInternalBidirectional(
578
        ResourceType $sourceResourceType,
579
        ResourceType $targetResourceType,
580
        $sourceProperty,
581
        $targetProperty,
582
        $sourcePropertyKind,
583
        $targetPropertyKind
584
    ) {
585
        if (!is_string($sourceProperty) || !is_string($targetProperty)) {
586
            throw new InvalidOperationException("Source and target properties must both be strings");
587
        }
588
589
        $this->checkInstanceProperty($sourceProperty, $sourceResourceType);
590
        $this->checkInstanceProperty($targetProperty, $targetResourceType);
591
592
        // check that property and resource name don't up and collide - would violate OData spec
593
        if (strtolower($sourceProperty) == strtolower($sourceResourceType->getName())) {
594
            throw new InvalidOperationException(
595
                'Source property name must be different from source resource name.'
596
            );
597
        }
598
        if (strtolower($targetProperty) == strtolower($targetResourceType->getName())) {
599
            throw new InvalidOperationException(
600
                'Target property name must be different from target resource name.'
601
            );
602
        }
603
604
        //Create instance of AssociationSet for this relationship
605
        $sourceResourceSet = $sourceResourceType->getCustomState();
606
        if (!$sourceResourceSet instanceof ResourceSet) {
607
            throw new InvalidOperationException(
608
                'Failed to retrieve the custom state from '
609
                . $sourceResourceType->getName()
610
            );
611
        }
612
        $targetResourceSet = $targetResourceType->getCustomState();
613
        if (!$targetResourceSet instanceof ResourceSet) {
614
            throw new InvalidOperationException(
615
                'Failed to retrieve the custom state from '
616
                . $targetResourceType->getName()
617
            );
618
        }
619
620
        //Customer_Orders_Orders, Order_Customer_Customers
621
        $fwdSetKey = ResourceAssociationSet::keyName($sourceResourceType, $sourceProperty, $targetResourceSet);
622
        $revSetKey = ResourceAssociationSet::keyName($targetResourceType, $targetProperty, $sourceResourceSet);
623
        if (isset($this->associationSets[$fwdSetKey]) && $this->associationSets[$revSetKey]) {
624
            return;
625
        }
626
627
        $sourceResourceProperty = new ResourceProperty($sourceProperty, null, $sourcePropertyKind, $targetResourceType);
628
        $sourceResourceType->addProperty($sourceResourceProperty, false);
629
        $targetResourceProperty = new ResourceProperty($targetProperty, null, $targetPropertyKind, $sourceResourceType);
630
        $targetResourceType->addProperty($targetResourceProperty, false);
631
632
633
        $fwdSet = new ResourceAssociationSet(
634
            $fwdSetKey,
635
            new ResourceAssociationSetEnd($sourceResourceSet, $sourceResourceType, $sourceResourceProperty),
636
            new ResourceAssociationSetEnd($targetResourceSet, $targetResourceType, $targetResourceProperty)
637
        );
638
        $revSet = new ResourceAssociationSet(
639
            $revSetKey,
640
            new ResourceAssociationSetEnd($targetResourceSet, $targetResourceType, $targetResourceProperty),
641
            new ResourceAssociationSetEnd($sourceResourceSet, $sourceResourceType, $sourceResourceProperty)
642
        );
643
        $sourceName = $sourceResourceType->getFullName();
644
        $targetName = $targetResourceType->getFullName();
645
        $sourceMult = $sourcePropertyKind == ResourcePropertyKind::RESOURCESET_REFERENCE ? '*' : '0..1';
646
        $targetMult = $targetPropertyKind == ResourcePropertyKind::RESOURCESET_REFERENCE ? '*' : '0..1';
647
        $this->metadataManager->addNavigationPropertyToEntityType(
648
            $this->OdataEntityMap[$sourceName],
649
            $sourceMult,
650
            $sourceProperty,
651
            $this->OdataEntityMap[$targetName],
652
            $targetMult,
653
            $targetProperty
654
        );
655
        $this->associationSets[$fwdSetKey] = $fwdSet;
656
        $this->associationSets[$revSetKey] = $revSet;
657
    }
658
659
    /**
660
     * To add a resource set reference property.
661
     *
662
     * @param ResourceType $resourceType The resource type to add the
663
     *                                        resource reference set property to
664
     * @param string $name The name of the property to add
665
     * @param ResourceSet $targetResourceSet The resource set the resource
666
     *                                        reference set property points to
667
     */
668
    public function addResourceSetReferenceProperty($resourceType, $name, $targetResourceSet)
669
    {
670
        $this->_addReferencePropertyInternal(
671
            $resourceType,
672
            $name,
673
            $targetResourceSet,
674
            ResourcePropertyKind::RESOURCESET_REFERENCE
675
        );
676
    }
677
678
    /**
679
     * To add a M:N resource reference property.
680
     *
681
     * @param ResourceType $sourceResourceType  The resource type to add the resource
682
     *                                          reference property from
683
     * @param ResourceType $targetResourceType  The resource type to add the resource
684
     *                                          reference property to
685
     * @param string $sourceProperty            The name of the property to add, on source type
686
     * @param string $targetProperty            The name of the property to add, on target type
687
     */
688
    public function addResourceSetReferencePropertyBidirectional(
689
        ResourceType $sourceResourceType,
690
        ResourceType $targetResourceType,
691
        $sourceProperty,
692
        $targetProperty
693
    ) {
694
        $this->_addReferencePropertyInternalBidirectional(
695
            $sourceResourceType,
696
            $targetResourceType,
697
            $sourceProperty,
698
            $targetProperty,
699
            ResourcePropertyKind::RESOURCESET_REFERENCE,
700
            ResourcePropertyKind::RESOURCESET_REFERENCE
701
        );
702
    }
703
704
    /**
705
     * To add a 1-1 resource reference.
706
     *
707
     * @param ResourceType $sourceResourceType  The resource type to add the resource
708
     *                                          reference property from
709
     * @param ResourceType $targetResourceType  The resource type to add the resource
710
     *                                          reference property to
711
     * @param string $sourceProperty            The name of the property to add, on source type
712
     * @param string $targetProperty            The name of the property to add, on target type
713
     */
714
    public function addResourceReferenceSinglePropertyBidirectional(
715
        ResourceType $sourceResourceType,
716
        ResourceType $targetResourceType,
717
        $sourceProperty,
718
        $targetProperty
719
    ) {
720
        $this->_addReferencePropertyInternalBidirectional(
721
            $sourceResourceType,
722
            $targetResourceType,
723
            $sourceProperty,
724
            $targetProperty,
725
            ResourcePropertyKind::RESOURCE_REFERENCE,
726
            ResourcePropertyKind::RESOURCE_REFERENCE
727
        );
728
    }
729
730
    /**
731
     * To add a complex property to entity or complex type.
732
     *
733
     * @param ResourceType $resourceType The resource type to which the
734
     *                                          complex property needs to add
735
     * @param string $name name of the complex property
736
     * @param ResourceType $complexResourceType complex resource type
737
     * @param bool $isBag complex type is bag or not
738
     *
739
     * @return ResourceProperty
740
     */
741
    public function addComplexProperty($resourceType, $name, $complexResourceType, $isBag = false)
742
    {
743
        if ($resourceType->getResourceTypeKind() != ResourceTypeKind::ENTITY
744
            && $resourceType->getResourceTypeKind() != ResourceTypeKind::COMPLEX
745
        ) {
746
            throw new InvalidOperationException('Complex property can be added to an entity or another complex type');
747
        }
748
749
        // check that property and resource name don't up and collide - would violate OData spec
750
        if (strtolower($name) == strtolower($resourceType->getName())) {
751
            throw new InvalidOperationException(
752
                'Property name must be different from resource name.'
753
            );
754
        }
755
756
        $this->checkInstanceProperty($name, $resourceType);
757
758
        $kind = ResourcePropertyKind::COMPLEX_TYPE;
759
        if ($isBag) {
760
            $kind = $kind | ResourcePropertyKind::BAG;
761
        }
762
763
        $resourceProperty = new ResourceProperty($name, null, $kind, $complexResourceType);
764
        $resourceType->addProperty($resourceProperty);
765
766
        return $resourceProperty;
767
    }
768
}
769