getResourceFromRelatedResourceSet()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 13
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 6
nc 1
nop 5
dl 0
loc 13
rs 10
c 0
b 0
f 0
1
<?php
2
3
declare(strict_types=1);
4
5
namespace POData\Providers;
6
7
use Exception;
8
use POData\Common\InvalidOperationException;
9
use POData\Common\Messages;
10
use POData\Common\ODataException;
11
use POData\Configuration\IServiceConfiguration;
12
use POData\Providers\Expression\IExpressionProvider;
13
use POData\Providers\Metadata\EdmSchemaVersion;
14
use POData\Providers\Metadata\IMetadataProvider;
15
use POData\Providers\Metadata\ResourceAssociationSet;
16
use POData\Providers\Metadata\ResourceEntityType;
17
use POData\Providers\Metadata\ResourceFunctionType;
18
use POData\Providers\Metadata\ResourceProperty;
19
use POData\Providers\Metadata\ResourceSet;
20
use POData\Providers\Metadata\ResourceSetWrapper;
21
use POData\Providers\Metadata\ResourceType;
22
use POData\Providers\Query\IQueryProvider;
23
use POData\Providers\Query\QueryResult;
24
use POData\Providers\Query\QueryType;
25
use POData\UriProcessor\QueryProcessor\ExpressionParser\FilterInfo;
26
use POData\UriProcessor\QueryProcessor\OrderByParser\InternalOrderByInfo;
27
use POData\UriProcessor\QueryProcessor\SkipTokenParser\SkipTokenInfo;
28
use POData\UriProcessor\ResourcePathProcessor\SegmentParser\KeyDescriptor;
29
use ReflectionException;
30
use TypeError;
31
32
/**
33
 * Class ProvidersWrapper.
34
 *
35
 * A wrapper class over IMetadataProvider and IQueryProvider implementations, All calls to implementation of methods
36
 * of these interfaces should go through this wrapper class so that wrapper methods of this class can perform validation
37
 */
38
class ProvidersWrapper
39
{
40
    /**
41
     * Holds reference to IMetadataProvider implementation.
42
     *
43
     * @var IMetadataProvider
44
     */
45
    private $metaProvider;
46
47
    /**
48
     * Holds reference to IServiceConfiguration implementation.
49
     *
50
     * @var IServiceConfiguration
51
     */
52
    private $config;
53
54
    /**
55
     * Holds reference to ProvidersQueryWrapper implementation.
56
     *
57
     * @var ProvidersQueryWrapper
58
     */
59
    private $providerWrapper;
60
61
    /**
62
     * Cache for ResourceProperties of a resource type that belongs to a
63
     * resource set. An entry (ResourceProperty collection) in this cache
64
     * contains only the visible properties of ResourceType.
65
     *
66
     * @var array<array>
67
     */
68
    private $propertyCache = [];
69
70
    /**
71
     * Cache for ResourceSetWrappers. If ResourceSet is invisible value will
72
     * be null.
73
     *
74
     * @var ResourceSetWrapper[] indexed by resource set name
75
     */
76
    private $setWrapperCache = [];
77
78
    /**
79
     * Cache for ResourceTypes.
80
     *
81
     * @var ResourceType[] indexed by resource type name
82
     */
83
    private $typeCache = [];
84
85
    /**
86
     * Creates a new instance of ProvidersWrapper.
87
     *
88
     * @param IMetadataProvider     $meta   Reference to IMetadataProvider implementation
89
     * @param IQueryProvider        $query  Reference to IQueryProvider implementation
90
     * @param IServiceConfiguration $config Reference to IServiceConfiguration implementation
91
     */
92
    public function __construct(IMetadataProvider $meta, IQueryProvider $query, IServiceConfiguration $config)
93
    {
94
        $this->metaProvider    = $meta;
95
        $this->config          = $config;
96
        $this->providerWrapper = new ProvidersQueryWrapper($query);
97
    }
98
99
    /**
100
     * To get the Container name for the data source,
101
     * Note: Wrapper for IMetadataProvider::getContainerName method
102
     * implementation.
103
     *
104
     * @throws ODataException Exception if implementation returns empty container name
105
     * @return string         that contains the name of the container
106
     */
107
    public function getContainerName()
108
    {
109
        $containerName = $this->getMetaProvider()->getContainerName();
110
        if (empty($containerName)) {
111
            throw new ODataException(
112
                Messages::providersWrapperContainerNameMustNotBeNullOrEmpty(),
113
                500
114
            );
115
        }
116
117
        return $containerName;
118
    }
119
120
    /**
121
     * @return IMetadataProvider
122
     */
123
    public function getMetaProvider()
124
    {
125
        assert(null != $this->metaProvider, 'Metadata provider must be set');
126
        return $this->metaProvider;
127
    }
128
129
    //Wrappers for IMetadataProvider methods
130
131
    /**
132
     * To get Namespace name for the data source,
133
     * Note: Wrapper for IMetadataProvider::getContainerNamespace method implementation.
134
     *
135
     * @throws ODataException Exception if implementation returns empty container namespace
136
     * @return string         that contains the namespace name
137
     */
138
    public function getContainerNamespace()
139
    {
140
        $containerNamespace = $this->getMetaProvider()->getContainerNamespace();
141
        if (empty($containerNamespace)) {
142
            throw new ODataException(
143
                Messages::providersWrapperContainerNamespaceMustNotBeNullOrEmpty(),
144
                500
145
            );
146
        }
147
148
        return $containerNamespace;
149
    }
150
151
    /**
152
     * To get the data service configuration.
153
     *
154
     * @return IServiceConfiguration
155
     */
156
    public function getConfiguration()
157
    {
158
        return $this->config;
159
    }
160
161
    /**
162
     *  To get all entity set information,
163
     *  Note: Wrapper for IMetadataProvider::getResourceSets method implementation,
164
     *  This method returns array of ResourceSetWrapper instances but the corresponding IDSMP method
165
     *  returns array of ResourceSet instances.
166
     *
167
     * @throws ODataException       when two resource sets with the same name are encountered
168
     * @return ResourceSetWrapper[] The ResourceSetWrappers for the visible ResourceSets
169
     */
170
    public function getResourceSets()
171
    {
172
        $resourceSets        = $this->getMetaProvider()->getResourceSets();
173
        $resourceSetWrappers = [];
174
        $resourceSetNames    = [];
175
        foreach ($resourceSets as $resourceSet) {
176
            $name = $resourceSet->getName();
177
            if (in_array($name, $resourceSetNames)) {
178
                throw new ODataException(Messages::providersWrapperEntitySetNameShouldBeUnique($name), 500);
179
            }
180
181
            $resourceSetNames[] = $name;
182
            $resourceSetWrapper = $this->validateResourceSetAndGetWrapper($resourceSet);
183
            if (null !== $resourceSetWrapper) {
184
                $resourceSetWrappers[] = $resourceSetWrapper;
185
            }
186
        }
187
188
        return $resourceSetWrappers;
189
    }
190
191
    /**
192
     * Wrapper function over _validateResourceSetAndGetWrapper function.
193
     *
194
     * @param ResourceSet $resourceSet see the comments of _validateResourceSetAndGetWrapper
195
     *
196
     * @throws ODataException
197
     * @return ResourceSetWrapper|null see the comments of _validateResourceSetAndGetWrapper
198
     */
199
    public function validateResourceSetAndGetWrapper(ResourceSet $resourceSet)
200
    {
201
        return $this->validateResourceSetAndWrapper($resourceSet);
202
    }
203
204
    /**
205
     * This function perform the following operations
206
     *  (1) If the cache contain an entry [key, value] for the resourceset then
207
     *      return the entry-value
208
     *  (2) If the cache not contain an entry for the resourceset then validate
209
     *      the resourceset
210
     *            (a) If valid add entry as [resouceset_name, resourceSetWrapper]
211
     *            (b) if not valid add entry as [resouceset_name, null]
212
     *  Note: validating a resourceset means checking the resourceset is visible
213
     *  or not using configuration.
214
     *
215
     * @param ResourceSet $resourceSet The resourceset to validate and get the
216
     *                                 wrapper for
217
     *
218
     * @throws ODataException
219
     * @return ResourceSetWrapper|null Returns an instance if a resource set with the given name is visible
220
     */
221
    private function validateResourceSetAndWrapper(ResourceSet $resourceSet)
222
    {
223
        $cacheKey = $resourceSet->getName();
224
        if (array_key_exists($cacheKey, $this->setWrapperCache)) {
225
            return $this->setWrapperCache[$cacheKey];
226
        }
227
228
        $this->validateResourceType($resourceSet->getResourceType());
229
        $wrapper                          = new ResourceSetWrapper($resourceSet, $this->config);
230
        $nuVal                            = $wrapper->isVisible() ? $wrapper : null;
231
        $this->setWrapperCache[$cacheKey] = $nuVal;
232
233
        return $this->setWrapperCache[$cacheKey];
234
    }
235
236
    /**
237
     * Validates the given instance of ResourceType.
238
     *
239
     * @param ResourceType $resourceType The ResourceType to validate
240
     *
241
     * @throws ODataException Exception if $resourceType is invalid
242
     * @return ResourceType
243
     */
244
    private function validateResourceType(ResourceType $resourceType)
245
    {
246
        $cacheKey = $resourceType->getName();
247
        if (array_key_exists($cacheKey, $this->typeCache)) {
248
            return $this->typeCache[$cacheKey];
249
        }
250
251
        //TODO: Do validation if any for the ResourceType
252
        $this->typeCache[$cacheKey] = $resourceType;
253
254
        return $resourceType;
255
    }
256
257
    /**
258
     * To get all resource types in the data source,
259
     * Note: Wrapper for IMetadataProvider::getTypes method implementation.
260
     *
261
     * @throws ODataException
262
     * @return ResourceType[]
263
     */
264
    public function getTypes()
265
    {
266
        $resourceTypes     = $this->getMetaProvider()->getTypes();
267
        $resourceTypeNames = [];
268
        foreach ($resourceTypes as $resourceType) {
269
            if (in_array($resourceType->getName(), $resourceTypeNames)) {
270
                throw new ODataException(
271
                    Messages::providersWrapperEntityTypeNameShouldBeUnique($resourceType->getName()),
272
                    500
273
                );
274
            }
275
276
            $resourceTypeNames[] = $resourceType->getName();
277
            $this->validateResourceType($resourceType);
278
        }
279
280
        return $resourceTypes;
281
    }
282
283
    /**
284
     * @return ResourceFunctionType[]
285
     */
286
    public function getSingletons()
287
    {
288
        $singletons = $this->getMetaProvider()->getSingletons();
289
        return (null == $singletons) ? [] : $singletons;
290
    }
291
292
    /**
293
     * To get a resource set based on the specified resource set name which is
294
     * visible,
295
     * Note: Wrapper for IMetadataProvider::resolveResourceSet method
296
     * implementation.
297
     *
298
     * @param string $name Name of the resource set
299
     *
300
     * @throws ODataException
301
     * @return ResourceSetWrapper|null Returns resource set with the given name if found,
302
     *                                 NULL if resource set is set to invisible or not found
303
     */
304
    public function resolveResourceSet($name): ?ResourceSetWrapper
305
    {
306
        if (array_key_exists($name, $this->setWrapperCache)) {
307
            return $this->setWrapperCache[$name];
308
        }
309
310
        $resourceSet = $this->getMetaProvider()->resolveResourceSet($name);
311
        if (null === $resourceSet) {
312
            return null;
313
        }
314
315
        return $this->validateResourceSetAndWrapper($resourceSet);
316
    }
317
318
    /**
319
     * To get a resource type based on the resource set name,
320
     * Note: Wrapper for IMetadataProvider::resolveResourceType
321
     * method implementation.
322
     *
323
     * @param string $name Name of the resource set
324
     *
325
     * @throws ODataException    If the ResourceType is invalid
326
     * @return ResourceType|null resource type with the given resource set name if found else NULL
327
     */
328
    public function resolveResourceType($name)
329
    {
330
        $resourceType = $this->getMetaProvider()->resolveResourceType($name);
331
        if (null === $resourceType) {
332
            return null;
333
        }
334
335
        return $this->validateResourceType($resourceType);
336
    }
337
338
    /**
339
     * Try to resolve named singleton.
340
     *
341
     * @param  string     $name
342
     * @return mixed|null
343
     */
344
    public function resolveSingleton($name)
345
    {
346
        $singletons = $this->getMetaProvider()->getSingletons();
347
        if (array_key_exists($name, $singletons)) {
348
            return $singletons[$name];
349
        }
350
        return null;
351
    }
352
353
    /**
354
     * The method must return a collection of all the types derived from
355
     * $resourceType The collection returned should NOT include the type
356
     * passed in as a parameter
357
     * Note: Wrapper for IMetadataProvider::getDerivedTypes
358
     * method implementation.
359
     *
360
     * @param ResourceEntityType $resourceType Resource to get derived resource types from
361
     *
362
     * @throws ODataException
363
     * @throws \TypeError     when the meat provider doesn't return an array
364
     * @return ResourceType[]
365
     */
366
    public function getDerivedTypes(ResourceEntityType $resourceType)
367
    {
368
        $derivedTypes = $this->getMetaProvider()->getDerivedTypes($resourceType);
369
370
        foreach ($derivedTypes as $derivedType) {
371
            $this->validateResourceType($derivedType);
372
        }
373
374
        return $derivedTypes;
375
    }
376
377
    /**
378
     * Returns true if $resourceType represents an Entity Type which has derived
379
     * Entity Types, else false.
380
     * Note: Wrapper for IMetadataProvider::hasDerivedTypes method implementation.
381
     *
382
     * @param ResourceEntityType $resourceType Resource to check for derived resource types
383
     *
384
     * @throws ODataException If the ResourceType is invalid
385
     * @return bool
386
     */
387
    public function hasDerivedTypes(ResourceEntityType $resourceType)
388
    {
389
        $this->validateResourceType($resourceType);
390
391
        return $this->getMetaProvider()->hasDerivedTypes($resourceType);
392
    }
393
394
    /**
395
     * Gets the visible resource properties for the given resource type from the given resource set wrapper.
396
     *
397
     * @param ResourceSetWrapper $setWrapper   Resource set wrapper in question
398
     * @param ResourceType       $resourceType Resource type in question
399
     *
400
     * @return ResourceProperty[] Collection of visible resource properties from the given resource set wrapper
401
     *                            and resource type
402
     */
403
    public function getResourceProperties(ResourceSetWrapper $setWrapper, ResourceType $resourceType)
404
    {
405
        if (!$resourceType instanceof ResourceEntityType) {
406
            //Complex resource type
407
            return $resourceType->getAllProperties();
408
        }
409
        //TODO: move this to doctrine annotations
410
        $cacheKey = $setWrapper->getName() . '_' . $resourceType->getFullName();
411
        if (!array_key_exists($cacheKey, $this->propertyCache)) {
412
            //Fill the cache
413
            $this->propertyCache[$cacheKey] = [];
414
            foreach ($resourceType->getAllProperties() as $resourceProperty) {
415
                $this->propertyCache[$cacheKey][$resourceProperty->getName()] = $resourceProperty;
416
            }
417
        }
418
419
        return $this->propertyCache[$cacheKey];
420
    }
421
422
    /**
423
     * Gets the target resource set wrapper for the given navigation property,
424
     * source resource set wrapper and the source resource type.
425
     *
426
     * @param ResourceSetWrapper $resourceSetWrapper         Source resource set
427
     * @param ResourceEntityType $resourceType               Source resource type
428
     * @param ResourceProperty   $navigationResourceProperty Navigation property
429
     *
430
     * @throws ODataException
431
     * @return ResourceSetWrapper|null Returns instance of ResourceSetWrapper
432
     *                                 (describes the entity set and associated configuration) for the
433
     *                                 given navigation property. returns NULL if resourceset for the
434
     *                                 navigation property is invisible or if metadata provider returns
435
     *                                 null resource association set
436
     */
437
    public function getResourceSetWrapperForNavigationProperty(
438
        ResourceSetWrapper $resourceSetWrapper,
439
        ResourceEntityType $resourceType,
440
        ResourceProperty $navigationResourceProperty
441
    ) {
442
        $associationSet = $this->getResourceAssociationSet(
443
            $resourceSetWrapper,
444
            $resourceType,
445
            $navigationResourceProperty
446
        );
447
448
        if (null !== $associationSet) {
449
            $relatedAssociationSetEnd = $associationSet->getRelatedResourceAssociationSetEnd(
450
                $resourceSetWrapper->getResourceSet(),
451
                $resourceType,
452
                $navigationResourceProperty
453
            );
454
455
            return $this->validateResourceSetAndWrapper(
456
                $relatedAssociationSetEnd->getResourceSet()
457
            );
458
        }
459
        return null;
460
    }
461
462
    /**
463
     * Gets the ResourceAssociationSet instance for the given source association end,
464
     * Note: Wrapper for IMetadataProvider::getResourceAssociationSet
465
     * method implementation.
466
     *
467
     * @param ResourceSet        $set      Resource set of the source association end
468
     * @param ResourceEntityType $type     Resource type of the source association end
469
     * @param ResourceProperty   $property Resource property of the source association end
470
     *
471
     * @throws ODataException
472
     * @return ResourceAssociationSet|null Returns ResourceAssociationSet for the source
473
     *                                     association end, NULL if no such
474
     *                                     association end or resource set in the
475
     *                                     other end of the association is invisible
476
     */
477
    public function getResourceAssociationSet(
478
        ResourceSet $set,
479
        ResourceEntityType $type,
480
        ResourceProperty $property
481
    ) {
482
        $type = $this->getResourceTypeWherePropertyIsDeclared($type, $property);
483
        // usage below requires $type to not be null - so kaboom as early as possible
484
        assert(null != $type, 'Resource type obtained from property must not be null.');
485
        assert($type instanceof ResourceEntityType);
486
487
        $associationSet = $this->getMetaProvider()->getResourceAssociationSet(
488
            $set,
489
            $type,
490
            $property
491
        );
492
        assert(
493
            null == $associationSet || $associationSet instanceof ResourceAssociationSet,
494
            'Retrieved resource association must be either null or an instance of ResourceAssociationSet'
495
        );
496
497
        if (null !== $associationSet) {
498
            $thisAssociationSetEnd = $associationSet->getResourceAssociationSetEnd(
499
                $set,
500
                $type,
501
                $property
502
            );
503
504
            $relatedAssociationSetEnd = $associationSet->getRelatedResourceAssociationSetEnd(
505
                $set,
506
                $type,
507
                $property
508
            );
509
510
            //If either $thisAssociationSetEnd and/or $relatedAssociationSetEnd
511
            //is null means the associationset we got from the IDSMP::getResourceAssociationSet is invalid.
512
513
            //Return null, if either AssociationSet's End1 or End2's resourceset name
514
            //doesn't match the name of resource set wrapper (param1) and resource type is not assignable
515
            //from given resource type (param2)
516
            if (null === $thisAssociationSetEnd || null === $relatedAssociationSetEnd) {
517
                throw new ODataException(
518
                    Messages::providersWrapperIDSMPGetResourceSetReturnsInvalidResourceSet(
519
                        $set->getName(),
520
                        $type->getFullName(),
521
                        $property->getName()
522
                    ),
523
                    500
524
                );
525
            }
526
527
            $relatedResourceSetWrapper = $this->validateResourceSetAndWrapper(
528
                $relatedAssociationSetEnd->getResourceSet()
529
            );
530
            if ($relatedResourceSetWrapper === null) {
531
                $associationSet = null;
532
            } else {
533
                $this->validateResourceType($thisAssociationSetEnd->getResourceType());
534
                $this->validateResourceType($relatedAssociationSetEnd->getResourceType());
535
            }
536
        }
537
        assert(
538
            null == $associationSet || $associationSet instanceof ResourceAssociationSet,
539
            'Retrieved resource assocation must be either null or an instance of ResourceAssociationSet'
540
        );
541
542
        return $associationSet;
543
    }
544
545
    /**
546
     * Gets the resource type on which the resource property is declared on,
547
     * If property is not declared in the given resource type, then this
548
     * function drill down to the inheritance hierarchy of the given resource
549
     * type to find out the base class in which the property is declared.
550
     *
551
     * @param ResourceType     $type     The resource type to start looking
552
     * @param ResourceProperty $property The resource property in question
553
     *
554
     * @return ResourceType|null Returns reference to the ResourceType on which
555
     *                           the $property is declared, NULL if
556
     *                           $property is not declared anywhere
557
     *                           in the inheritance hierarchy
558
     */
559
    protected function getResourceTypeWherePropertyIsDeclared(ResourceType $type, ResourceProperty $property)
560
    {
561
        while (null !== $type) {
562
            if (null !== $type->resolvePropertyDeclaredOnThisType($property->getName())) {
563
                break;
564
            }
565
566
            $type = $type->getBaseType();
567
        }
568
569
        return $type;
570
    }
571
572
    /**
573
     * Gets the Edm Schema version compliance to the metadata.
574
     *
575
     * @return EdmSchemaVersion
576
     */
577
    public function getEdmSchemaVersion()
578
    {
579
        //The minimal schema version for custom provider is 1.1
580
        return EdmSchemaVersion::VERSION_1_DOT_1();
581
    }
582
583
    /**
584
     * Gets the underlying custom expression provider, the end developer is
585
     * responsible for implementing IExpressionProvider if he choose for.
586
     *
587
     * @throws ODataException
588
     * @return IExpressionProvider Instance of IExpressionProvider implementation
589
     */
590
    public function getExpressionProvider()
591
    {
592
        return $this->getProviderWrapper()->getExpressionProvider();
593
    }
594
595
    /**
596
     * @return ProvidersQueryWrapper
597
     */
598
    public function getProviderWrapper()
599
    {
600
        assert(null != $this->providerWrapper, 'Provider wrapper must be set');
601
        return $this->providerWrapper;
602
    }
603
604
    /**
605
     * Indicates if the QueryProvider can handle ordered paging, this means respecting order, skip, and top parameters
606
     * If the query provider can not handle ordered paging, it must return the entire result set and POData will
607
     * perform the ordering and paging.
608
     *
609
     * @return bool True if the query provider can handle ordered paging, false if POData should perform the paging
610
     */
611
    public function handlesOrderedPaging()
612
    {
613
        return $this->getProviderWrapper()->handlesOrderedPaging();
614
    }
615
616
    /**
617
     * Gets collection of entities belongs to an entity set
618
     * IE: http://host/EntitySet
619
     *  http://host/EntitySet?$skip=10&$top=5&filter=Prop gt Value.
620
     *
621
     * @param QueryType                $queryType   Is this is a query for a count, entities, or entities-with-count
622
     * @param ResourceSet              $resourceSet The entity set containing the entities to fetch
623
     * @param FilterInfo|null          $filterInfo  The $filter parameter of the OData query.  NULL if none specified
624
     * @param null|InternalOrderByInfo $orderBy     sorted order if we want to get the data in some specific order
625
     * @param int|null                 $top         number of records which need to be retrieved
626
     * @param int|null                 $skip        number of records which need to be skipped
627
     * @param SkipTokenInfo|null       $skipToken   value indicating what records to skip
628
     * @param string[]|null            $eagerLoad   array of relations to eager load
629
     *
630
     * @throws ODataException
631
     * @return QueryResult
632
     */
633
    public function getResourceSet(
634
        QueryType $queryType,
635
        ResourceSet $resourceSet,
636
        FilterInfo $filterInfo = null,
637
        InternalOrderByInfo $orderBy = null,
638
        $top = null,
639
        $skip = null,
640
        SkipTokenInfo $skipToken = null,
641
        array $eagerLoad = []
642
    ) {
643
        return $this->getProviderWrapper()->getResourceSet(
644
            $queryType,
645
            $resourceSet,
646
            $filterInfo,
647
            $orderBy,
648
            $top,
649
            $skip,
650
            $skipToken,
651
            $eagerLoad
652
        );
653
    }
654
655
    /**
656
     * Gets an entity instance from an entity set identified by a key.
657
     *
658
     * @param ResourceSet   $resourceSet   The entity set containing the entity to fetch
659
     * @param KeyDescriptor $keyDescriptor The key identifying the entity to fetch
660
     * @param string[]|null $eagerLoad     array of relations to eager load
661
     *
662
     * @throws InvalidOperationException
663
     * @throws ReflectionException
664
     * @throws ODataException
665
     * @return object|null               Returns entity instance if found, else null
666
     */
667
    public function getResourceFromResourceSet(
668
        ResourceSet $resourceSet,
669
        KeyDescriptor $keyDescriptor,
670
        array $eagerLoad = null
671
    ) {
672
        return $this->getProviderWrapper()->getResourceFromResourceSet($resourceSet, $keyDescriptor, $eagerLoad);
673
    }
674
675
    /**
676
     * Puts an entity instance to entity set identified by a key.
677
     *
678
     * @param ResourceSet   $resourceSet   The entity set containing the entity to update
679
     * @param KeyDescriptor $keyDescriptor The key identifying the entity to update
680
     * @param mixed         $data
681
     *
682
     * @return bool|null Returns result of executing query
683
     */
684
    public function putResource(
685
        ResourceSet $resourceSet,
686
        KeyDescriptor $keyDescriptor,
687
        $data
688
    ) {
689
        return $this->getProviderWrapper()->putResource(
690
            $resourceSet,
691
            $keyDescriptor,
692
            $data
693
        );
694
    }
695
696
    /**
697
     * Get related resource set for a resource.
698
     *
699
     * @param QueryType          $queryType         Indicates if this is a query for a count, entities, or entities
700
     *                                              with a count
701
     * @param ResourceSet        $sourceResourceSet The entity set containing the source entity
702
     * @param object             $sourceEntity      The source entity instance
703
     * @param ResourceSet        $targetResourceSet The resource set containing the target of the navigation property
704
     * @param ResourceProperty   $targetProperty    The navigation property to retrieve
705
     * @param FilterInfo|null    $filterInfo        Represents the $filter parameter of the OData query.
706
     *                                              NULL if no $filter specified
707
     * @param mixed|null         $orderBy           sorted order if we want to get the data in some specific order
708
     * @param int|null           $top               number of records which need to be retrieved
709
     * @param int|null           $skip              number of records which need to be skipped
710
     * @param SkipTokenInfo|null $skipToken         value indicating what records to skip
711
     *
712
     * @throws ODataException
713
     * @return QueryResult
714
     */
715
    public function getRelatedResourceSet(
716
        QueryType $queryType,
717
        ResourceSet $sourceResourceSet,
718
        $sourceEntity,
719
        ResourceSet $targetResourceSet,
720
        ResourceProperty $targetProperty,
721
        FilterInfo $filterInfo = null,
722
        $orderBy = null,
723
        $top = null,
724
        $skip = null,
725
        SkipTokenInfo $skipToken = null
726
    ) {
727
        return $this->getProviderWrapper()->getRelatedResourceSet(
728
            $queryType,
729
            $sourceResourceSet,
730
            $sourceEntity,
731
            $targetResourceSet,
732
            $targetProperty,
733
            $filterInfo,
734
            $orderBy,
735
            $top,
736
            $skip,
737
            $skipToken
738
        );
739
    }
740
741
    /**
742
     * Gets a related entity instance from an entity set identified by a key.
743
     *
744
     * @param ResourceSet      $sourceResourceSet The entity set related to the entity to be fetched
745
     * @param object           $sourceEntity      The related entity instance
746
     * @param ResourceSet      $targetResourceSet The entity set from which entity needs to be fetched
747
     * @param ResourceProperty $targetProperty    The metadata of the target property
748
     * @param KeyDescriptor    $keyDescriptor     The key to identify the entity to be fetched
749
     *
750
     * @throws InvalidOperationException
751
     * @throws ReflectionException
752
     * @throws ODataException
753
     * @return object|null               Returns entity instance if found, else null
754
     */
755
    public function getResourceFromRelatedResourceSet(
756
        ResourceSet $sourceResourceSet,
757
        $sourceEntity,
758
        ResourceSet $targetResourceSet,
759
        ResourceProperty $targetProperty,
760
        KeyDescriptor $keyDescriptor
761
    ) {
762
        return $this->getProviderWrapper()->getResourceFromRelatedResourceSet(
763
            $sourceResourceSet,
764
            $sourceEntity,
765
            $targetResourceSet,
766
            $targetProperty,
767
            $keyDescriptor
768
        );
769
    }
770
771
    /**
772
     * Get related resource for a resource.
773
     *
774
     * @param ResourceSet      $sourceResourceSet The source resource set
775
     * @param object           $sourceEntity      The source resource
776
     * @param ResourceSet      $targetResourceSet The resource set of the navigation
777
     *                                            property
778
     * @param ResourceProperty $targetProperty    The navigation property to be
779
     *                                            retrieved
780
     *
781
     * @throws InvalidOperationException
782
     * @throws ReflectionException
783
     * @throws ODataException
784
     * @return object|null               The related resource if exists, else null
785
     */
786
    public function getRelatedResourceReference(
787
        ResourceSet $sourceResourceSet,
788
        $sourceEntity,
789
        ResourceSet $targetResourceSet,
790
        ResourceProperty $targetProperty
791
    ) {
792
        return $this->getProviderWrapper()->getRelatedResourceReference(
793
            $sourceResourceSet,
794
            $sourceEntity,
795
            $targetResourceSet,
796
            $targetProperty
797
        );
798
    }
799
800
    /**
801
     * Updates a resource.
802
     *
803
     * @param ResourceSet   $sourceResourceSet    The entity set containing the source entity
804
     * @param object        $sourceEntityInstance The source entity instance
805
     * @param KeyDescriptor $keyDescriptor        The key identifying the entity to fetch
806
     * @param object        $data                 the New data for the entity instance
807
     * @param bool          $shouldUpdate         Should undefined values be updated or reset to default
808
     *
809
     * @return object|null the new resource value if it is assignable, or throw exception for null
810
     */
811
    public function updateResource(
812
        ResourceSet $sourceResourceSet,
813
        $sourceEntityInstance,
814
        KeyDescriptor $keyDescriptor,
815
        $data,
816
        $shouldUpdate = false
817
    ) {
818
        return $this->getProviderWrapper()->updateResource(
819
            $sourceResourceSet,
820
            $sourceEntityInstance,
821
            $keyDescriptor,
822
            $data,
823
            $shouldUpdate
824
        );
825
    }
826
827
    /**
828
     * Delete resource from a resource set.
829
     *
830
     * @param ResourceSet $sourceResourceSet
831
     * @param object      $sourceEntityInstance
832
     *
833
     * @return bool true if resources successfully deleted, otherwise false
834
     */
835
    public function deleteResource(
836
        ResourceSet $sourceResourceSet,
837
        $sourceEntityInstance
838
    ) {
839
        return $this->getProviderWrapper()->deleteResource(
840
            $sourceResourceSet,
841
            $sourceEntityInstance
842
        );
843
    }
844
845
    /**
846
     * @param ResourceSet $resourceSet          The entity set containing the entity to fetch
847
     * @param object|null $sourceEntityInstance The source entity instance
848
     * @param object      $data                 the New data for the entity instance
849
     *
850
     * @return object|null returns the newly created model if successful, or null if model creation failed
851
     */
852
    public function createResourceforResourceSet(
853
        ResourceSet $resourceSet,
854
        $sourceEntityInstance,
855
        $data
856
    ) {
857
        return $this->getProviderWrapper()->createResourceforResourceSet(
858
            $resourceSet,
859
            $sourceEntityInstance,
860
            $data
861
        );
862
    }
863
864
    /**
865
     * Create multiple new resources in a resource set.
866
     * @param ResourceSet $sourceResourceSet The entity set containing the entity to fetch
867
     * @param object[]    $data              The new data for the entity instance
868
     *
869
     * @return object[]|null returns the newly created model if successful, or null if model creation failed
870
     */
871
    public function createBulkResourceforResourceSet(
872
        ResourceSet $sourceResourceSet,
873
        array $data
874
    ) {
875
        return $this->getProviderWrapper()->createBulkResourceforResourceSet(
876
            $sourceResourceSet,
877
            $data
878
        );
879
    }
880
881
    /**
882
     * Updates a group of resources in a resource set.
883
     *
884
     * @param ResourceSet     $sourceResourceSet    The entity set containing the source entity
885
     * @param object          $sourceEntityInstance The source entity instance
886
     * @param KeyDescriptor[] $keyDescriptor        The key identifying the entity to fetch
887
     * @param object[]        $data                 The new data for the entity instances
888
     * @param bool            $shouldUpdate         Should undefined values be updated or reset to default
889
     *
890
     * @return object[]|null the new resource value if it is assignable, or throw exception for null
891
     */
892
    public function updateBulkResource(
893
        ResourceSet $sourceResourceSet,
894
        $sourceEntityInstance,
895
        array $keyDescriptor,
896
        array $data,
897
        $shouldUpdate = false
898
    ) {
899
        return $this->getProviderWrapper()->updateBulkResource(
900
            $sourceResourceSet,
901
            $sourceEntityInstance,
902
            $keyDescriptor,
903
            $data,
904
            $shouldUpdate
905
        );
906
    }
907
908
    /**
909
     * Attaches child model to parent model.
910
     *
911
     * @param ResourceSet $sourceResourceSet
912
     * @param object      $sourceEntityInstance
913
     * @param ResourceSet $targetResourceSet
914
     * @param object      $targetEntityInstance
915
     * @param $navPropName
916
     *
917
     * @return bool
918
     */
919
    public function hookSingleModel(
920
        ResourceSet $sourceResourceSet,
921
        $sourceEntityInstance,
922
        ResourceSet $targetResourceSet,
923
        $targetEntityInstance,
924
        $navPropName
925
    ) {
926
        return $this->getProviderWrapper()->hookSingleModel(
927
            $sourceResourceSet,
928
            $sourceEntityInstance,
929
            $targetResourceSet,
930
            $targetEntityInstance,
931
            $navPropName
932
        );
933
    }
934
935
    /**
936
     * Removes child model from parent model.
937
     *
938
     * @param ResourceSet $sourceResourceSet
939
     * @param object      $sourceEntityInstance
940
     * @param ResourceSet $targetResourceSet
941
     * @param object      $targetEntityInstance
942
     * @param $navPropName
943
     *
944
     * @return bool
945
     */
946
    public function unhookSingleModel(
947
        ResourceSet $sourceResourceSet,
948
        $sourceEntityInstance,
949
        ResourceSet $targetResourceSet,
950
        $targetEntityInstance,
951
        $navPropName
952
    ) {
953
        return $this->getProviderWrapper()->unhookSingleModel(
954
            $sourceResourceSet,
955
            $sourceEntityInstance,
956
            $targetResourceSet,
957
            $targetEntityInstance,
958
            $navPropName
959
        );
960
    }
961
962
    /**
963
     * @throws Exception
964
     * @return mixed
965
     */
966
    public function getMetadataXML()
967
    {
968
        return $this->getMetaProvider()->getXML();
969
    }
970
971
    /**
972
     * Start database transaction.
973
     *
974
     * @param  bool $isBulk Is this transaction inside a batch request?
975
     * @return void
976
     */
977
    public function startTransaction($isBulk = false)
978
    {
979
        $this->getProviderWrapper()->startTransaction($isBulk);
980
    }
981
982
    /**
983
     * Commit database transaction.
984
     *
985
     * @return void
986
     */
987
    public function commitTransaction()
988
    {
989
        $this->getProviderWrapper()->commitTransaction();
990
    }
991
992
    /**
993
     * Abort database transaction.
994
     *
995
     * @return void
996
     */
997
    public function rollBackTransaction()
998
    {
999
        $this->getProviderWrapper()->rollBackTransaction();
1000
    }
1001
}
1002