Test Setup Failed
Pull Request — master (#102)
by Alex
03:26
created

ProvidersQueryWrapper::getRelatedResourceSet()   B

Complexity

Conditions 1
Paths 1

Size

Total Lines 29
Code Lines 24

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 29
rs 8.8571
c 0
b 0
f 0
cc 1
eloc 24
nc 1
nop 10

How to fix   Many Parameters   

Many Parameters

Methods with many parameters are not only hard to understand, but their parameters also often become inconsistent when you need more, or different data.

There are several approaches to avoid long parameter lists:

1
<?php
2
3
namespace POData\Providers;
4
5
use POData\Common\Messages;
6
use POData\Common\ODataException;
7
use POData\Providers\Expression\IExpressionProvider;
8
use POData\Providers\Metadata\ResourceProperty;
9
use POData\Providers\Metadata\ResourceSet;
10
use POData\Providers\Metadata\ResourceType;
11
use POData\Providers\Query\IQueryProvider;
12
use POData\Providers\Query\QueryResult;
13
use POData\Providers\Query\QueryType;
14
use POData\UriProcessor\QueryProcessor\ExpressionParser\FilterInfo;
15
use POData\UriProcessor\QueryProcessor\OrderByParser\InternalOrderByInfo;
16
use POData\UriProcessor\QueryProcessor\SkipTokenParser\InternalSkipTokenInfo;
17
use POData\UriProcessor\QueryProcessor\SkipTokenParser\SkipTokenInfo;
18
use POData\UriProcessor\ResourcePathProcessor\SegmentParser\KeyDescriptor;
19
20
class ProvidersQueryWrapper
21
{
22
    /**
23
     * Holds reference to IQueryProvider implementation.
24
     *
25
     * @var IQueryProvider
26
     */
27
    private $queryProvider;
28
29
    /**
30
     * Creates a new instance of ProvidersWrapper.
31
     *
32
     * @param IQueryProvider $query Reference to IQueryProvider implementation
33
     */
34
    public function __construct(IQueryProvider $query)
35
    {
36
        $this->queryProvider = $query;
37
    }
38
39
    public function getQueryProvider()
40
    {
41
        return $this->queryProvider;
42
    }
43
44
    /**
45
     * Get related resource set for a resource.
46
     *
47
     * @param QueryType             $queryType         Indicates if this is a query for a count, entities, or entities
48
     *                                                  with a count
49
     * @param ResourceSet           $sourceResourceSet The entity set containing the source entity
50
     * @param object                $sourceEntity      The source entity instance
51
     * @param ResourceSet           $targetResourceSet The resource set containing the target of the navigation property
52
     * @param ResourceProperty      $targetProperty    The navigation property to retrieve
53
     * @param FilterInfo            $filterInfo        Represents the $filter parameter of the OData query.
54
     *                                                 NULL if no $filter specified
55
     * @param mixed                 $orderBy           sorted order if we want to get the data in some specific order
56
     * @param int                   $top               The top count
57
     * @param int                   $skip              The skip count
58
     * @param SkipTokenInfo|null    $skipToken         The skip token
59
     *
60
     * @throws ODataException
61
     *
62
     * @return QueryResult
63
     */
64
    public function getRelatedResourceSet(
65
        QueryType $queryType,
66
        ResourceSet $sourceResourceSet,
67
        $sourceEntity,
68
        ResourceSet $targetResourceSet,
69
        ResourceProperty $targetProperty,
70
        $filterInfo,
71
        $orderBy,
72
        $top,
73
        $skip,
74
        $skipToken = null
75
    ) {
76
        $queryResult = $this->getQueryProvider()->getRelatedResourceSet(
77
            $queryType,
78
            $sourceResourceSet,
79
            $sourceEntity,
80
            $targetResourceSet,
81
            $targetProperty,
82
            $filterInfo,
83
            $orderBy,
84
            $top,
85
            $skip,
86
            $skipToken
87
        );
88
89
        $this->validateQueryResult($queryResult, $queryType, 'IQueryProvider::getRelatedResourceSet');
90
91
        return $queryResult;
92
    }
93
94
    /**
95
     * Gets collection of entities belongs to an entity set.
96
     *
97
     * @param QueryType             $queryType   Indicates if this is a query for a count, entities, or entities with a
98
     *                                           count
99
     * @param ResourceSet           $resourceSet The entity set containing the entities that need to be fetched
100
     * @param FilterInfo            $filterInfo  Represents the $filter parameter of the OData query.
101
     *                                           NULL if no $filter specified
102
     * @param InternalOrderByInfo   $orderBy     The orderBy information
103
     * @param int                   $top         The top count
104
     * @param int                   $skip        The skip count
105
     * @param SkipTokenInfo|null    $skipToken   The skip token
106
     *
107
     * @return QueryResult
108
     */
109
    public function getResourceSet(
110
        QueryType $queryType,
111
        ResourceSet $resourceSet,
112
        FilterInfo $filterInfo = null,
113
        InternalOrderByInfo $orderBy = null,
114
        $top = null,
115
        $skip = null,
116
        SkipTokenInfo $skipToken = null
117
    ) {
118
        $queryResult = $this->getQueryProvider()->getResourceSet(
119
            $queryType,
120
            $resourceSet,
121
            $filterInfo,
122
            $orderBy,
123
            $top,
124
            $skip,
125
            $skipToken
126
        );
127
128
        $this->validateQueryResult($queryResult, $queryType, 'IQueryProvider::getResourceSet');
129
130
        return $queryResult;
131
    }
132
133
    /**
134
     * Puts an entity instance to entity set identified by a key.
135
     *
136
     * @param ResourceSet   $resourceSet   The entity set containing the entity to update
137
     * @param KeyDescriptor $keyDescriptor The key identifying the entity to update
138
     * @param $data
139
     *
140
     * @return bool|null Returns result of executiong query
141
     */
142
    public function putResource(
143
        ResourceSet $resourceSet,
144
        KeyDescriptor $keyDescriptor,
145
        $data
146
    ) {
147
        $queryResult = $this->getQueryProvider()->putResource(
148
            $resourceSet,
149
            $keyDescriptor,
150
            $data
151
        );
152
153
        return $queryResult;
154
    }
155
156
    /**
157
     * Indicates if the QueryProvider can handle ordered paging, this means respecting order, skip, and top parameters
158
     * If the query provider can not handle ordered paging, it must return the entire result set and POData will
159
     * perform the ordering and paging.
160
     *
161
     * @return bool True if the query provider can handle ordered paging, false if POData should perform the paging
162
     */
163
    public function handlesOrderedPaging()
164
    {
165
        return $this->getQueryProvider()->handlesOrderedPaging();
166
    }
167
168
    /**
169
     * Gets the underlying custom expression provider, the end developer is
170
     * responsible for implementing IExpressionProvider if he choose for.
171
     *
172
     * @throws ODataException
173
     *
174
     * @return IExpressionProvider Instance of IExpressionProvider implementation
175
     */
176
    public function getExpressionProvider()
177
    {
178
        $expressionProvider = $this->getQueryProvider()->getExpressionProvider();
179
        if (is_null($expressionProvider)) {
180
            throw ODataException::createInternalServerError(
181
                Messages::providersWrapperExpressionProviderMustNotBeNullOrEmpty()
182
            );
183
        }
184
185
        if (!$expressionProvider instanceof IExpressionProvider) {
186
            throw ODataException::createInternalServerError(
187
                Messages::providersWrapperInvalidExpressionProviderInstance()
188
            );
189
        }
190
191
        return $expressionProvider;
192
    }
193
194
    /**
195
     * @param ResourceSet $resourceSet          The entity set containing the entity to fetch
196
     * @param object      $sourceEntityInstance The source entity instance
197
     * @param object      $data                 The New data for the entity instance.
198
     *
199
     * returns object|null returns the newly created model if sucessful or null if model creation failed.
200
     */
201
    public function createResourceforResourceSet(
202
        ResourceSet $resourceSet,
203
        $sourceEntityInstance,
204
        $data
205
    ) {
206
        return $this->getQueryProvider()->createResourceforResourceSet(
207
            $resourceSet,
208
            $sourceEntityInstance,
209
            $data
210
        );
211
    }
212
213
    /**
214
     * Delete resource from a resource set.
215
     *
216
     * @param ResourceSet $sourceResourceSet
217
     * @param object      $sourceEntityInstance
218
     *
219
     * return bool true if resources sucessfully deteled, otherwise false.
220
     */
221
    public function deleteResource(
222
        ResourceSet $sourceResourceSet,
223
        $sourceEntityInstance
224
    ) {
225
        return $this->getQueryProvider()->deleteResource(
226
            $sourceResourceSet,
227
            $sourceEntityInstance
228
        );
229
    }
230
231
    /**
232
     * Updates a resource.
233
     *
234
     * @param ResourceSet   $sourceResourceSet    The entity set containing the source entity
235
     * @param object        $sourceEntityInstance The source entity instance
236
     * @param KeyDescriptor $keyDescriptor        The key identifying the entity to fetch
237
     * @param object        $data                 The New data for the entity instance.
238
     * @param bool          $shouldUpdate         Should undefined values be updated or reset to default
239
     *
240
     * @return object|null The new resource value if it is assignable or throw exception for null.
241
     */
242
    public function updateResource(
243
        ResourceSet $sourceResourceSet,
244
        $sourceEntityInstance,
245
        KeyDescriptor $keyDescriptor,
246
        $data,
247
        $shouldUpdate = false
248
    ) {
249
        return $this->getQueryProvider()->updateResource(
250
            $sourceResourceSet,
251
            $sourceEntityInstance,
252
            $keyDescriptor,
253
            $data,
254
            $shouldUpdate
255
        );
256
    }
257
258
    /**
259
     * Get related resource for a resource.
260
     *
261
     * @param ResourceSet      $sourceResourceSet The source resource set
262
     * @param object           $sourceEntity      The source resource
263
     * @param ResourceSet      $targetResourceSet The resource set of the navigation
264
     *                                            property
265
     * @param ResourceProperty $targetProperty    The navigation property to be
266
     *                                            retrieved
267
     *
268
     * @throws ODataException
269
     *
270
     * @return object|null The related resource if exists else null
271
     */
272
    public function getRelatedResourceReference(
273
        ResourceSet $sourceResourceSet,
274
        $sourceEntity,
275
        ResourceSet $targetResourceSet,
276
        ResourceProperty $targetProperty
277
    ) {
278
        $entityInstance = $this->getQueryProvider()->getRelatedResourceReference(
279
            $sourceResourceSet,
280
            $sourceEntity,
281
            $targetResourceSet,
282
            $targetProperty
283
        );
284
285
        // we will not throw error if the resource reference is null
286
        // e.g. Orders(1234)/Customer => Customer can be null, this is
287
        // allowed if Customer is last segment. consider the following:
288
        // Orders(1234)/Customer/Orders => here if Customer is null then
289
        // the UriProcessor will throw error.
290
        if (!is_null($entityInstance)) {
291
            $methodName = 'IQueryProvider::getRelatedResourceReference';
292
293
            $targetResourceType = $this->verifyResourceType($methodName, $entityInstance, $targetResourceSet);
294
            foreach ($targetProperty->getResourceType()->getKeyProperties() as $keyName => $resourceProperty) {
295
                try {
296
                    $keyValue = $targetResourceType->getPropertyValue($entityInstance, $keyName);
297
                    if (is_null($keyValue)) {
298
                        throw ODataException::createInternalServerError(
299
                            Messages::providersWrapperIDSQPMethodReturnsInstanceWithNullKeyProperties(
300
                                'IDSQP::getRelatedResourceReference'
301
                            )
302
                        );
303
                    }
304
                } catch (\ReflectionException $reflectionException) {
305
                    // Left blank - we're simply squashing reflection exceptions
306
                }
307
            }
308
        }
309
310
        return $entityInstance;
311
    }
312
313
    /**
314
     * Gets a related entity instance from an entity set identified by a key.
315
     *
316
     * @param ResourceSet      $sourceResourceSet The entity set related to the entity to be fetched
317
     * @param object           $sourceEntity      The related entity instance
318
     * @param ResourceSet      $targetResourceSet The entity set from which entity needs to be fetched
319
     * @param ResourceProperty $targetProperty    The metadata of the target property
320
     * @param KeyDescriptor    $keyDescriptor     The key to identify the entity to be fetched
321
     *
322
     * @return object|null Returns entity instance if found else null
323
     */
324
    public function getResourceFromRelatedResourceSet(
325
        ResourceSet $sourceResourceSet,
326
        $sourceEntity,
327
        ResourceSet $targetResourceSet,
328
        ResourceProperty $targetProperty,
329
        KeyDescriptor $keyDescriptor
330
    ) {
331
        $entityInstance = $this->getQueryProvider()->getResourceFromRelatedResourceSet(
332
            $sourceResourceSet,
333
            $sourceEntity,
334
            $targetResourceSet,
335
            $targetProperty,
336
            $keyDescriptor
337
        );
338
339
        $this->validateEntityInstance(
340
            $entityInstance,
341
            $targetResourceSet,
342
            $keyDescriptor,
343
            'IQueryProvider::getResourceFromRelatedResourceSet'
344
        );
345
346
        return $entityInstance;
347
    }
348
349
    /**
350
     * Gets an entity instance from an entity set identified by a key.
351
     *
352
     * @param ResourceSet   $resourceSet   The entity set containing the entity to fetch
353
     * @param KeyDescriptor $keyDescriptor The key identifying the entity to fetch
354
     *
355
     * @return object|null Returns entity instance if found else null
356
     */
357
    public function getResourceFromResourceSet(ResourceSet $resourceSet, KeyDescriptor $keyDescriptor)
358
    {
359
        $entityInstance = $this->getQueryProvider()->getResourceFromResourceSet($resourceSet, $keyDescriptor);
360
        $this->validateEntityInstance(
361
            $entityInstance,
362
            $resourceSet,
363
            $keyDescriptor,
364
            'IQueryProvider::getResourceFromResourceSet'
365
        );
366
367
        return $entityInstance;
368
    }
369
370
    /**
371
     * @param QueryResult $queryResult
372
     * @param string      $methodName
373
     *
374
     * @throws ODataException
375
     */
376
    private function validateQueryResult($queryResult, QueryType $queryType, $methodName)
377
    {
378
        if (!$queryResult instanceof QueryResult) {
379
            throw ODataException::createInternalServerError(
380
                Messages::queryProviderReturnsNonQueryResult($methodName)
381
            );
382
        }
383
384
        $isResultArray = is_array($queryResult->results);
385
386
        if (QueryType::COUNT() == $queryType || QueryType::ENTITIES_WITH_COUNT() == $queryType) {
387
            //and the provider is supposed to handle the ordered paging they must return a count!
388
            if ($this->handlesOrderedPaging() && !is_numeric($queryResult->count)) {
389
                throw ODataException::createInternalServerError(
390
                    Messages::queryProviderResultCountMissing($methodName, $queryType)
391
                );
392
            }
393
394
            //If POData is supposed to handle the ordered aging they must return results! (possibly empty)
395
            if (!$this->handlesOrderedPaging() && !$isResultArray) {
396
                throw ODataException::createInternalServerError(
397
                    Messages::queryProviderResultsMissing($methodName, $queryType)
398
                );
399
            }
400
        }
401
402
        if ((QueryType::ENTITIES() == $queryType || QueryType::ENTITIES_WITH_COUNT() == $queryType)
403
            && !$isResultArray) {
404
            throw ODataException::createInternalServerError(
405
                Messages::queryProviderResultsMissing($methodName, $queryType)
406
            );
407
        }
408
    }
409
410
    /**
411
     * Validate the given entity instance.
412
     *
413
     * @param object|null   $entityInstance Entity instance to validate
414
     * @param ResourceSet   &$resourceSet   Resource set to which the entity
415
     *                                      instance belongs to
416
     * @param KeyDescriptor &$keyDescriptor The key descriptor
417
     * @param string        $methodName     Method from which this function
418
     *                                      invoked
419
     *
420
     * @throws ODataException
421
     */
422
    private function validateEntityInstance(
423
        $entityInstance,
424
        ResourceSet & $resourceSet,
425
        KeyDescriptor & $keyDescriptor,
426
        $methodName
427
    ) {
428
        if (is_null($entityInstance)) {
429
            throw ODataException::createResourceNotFoundError($resourceSet->getName());
430
        }
431
432
        $resourceType = $this->verifyResourceType($methodName, $entityInstance, $resourceSet);
433
434
        foreach ($keyDescriptor->getValidatedNamedValues() as $keyName => $valueDescription) {
435
            try {
436
                $keyValue = $resourceType->getPropertyValue($entityInstance, $keyName);
437
                if (is_null($keyValue)) {
438
                    throw ODataException::createInternalServerError(
439
                        Messages::providersWrapperIDSQPMethodReturnsInstanceWithNullKeyProperties($methodName)
440
                    );
441
                }
442
443
                $convertedValue = $valueDescription[1]->convert($valueDescription[0]);
444
                if ($keyValue != $convertedValue) {
445
                    throw ODataException::createInternalServerError(
446
                        Messages::providersWrapperIDSQPMethodReturnsInstanceWithNonMatchingKeys($methodName)
447
                    );
448
                }
449
            } catch (\ReflectionException $reflectionException) {
450
                // Left blank - we're simply squashing reflection exceptions
451
            }
452
        }
453
    }
454
455
    /**
456
     * @param string $methodName
457
     * @param $entityInstance
458
     * @param ResourceSet $resourceSet
459
     *
460
     * @throws ODataException
461
     *
462
     * @return ResourceType
463
     */
464
    private function verifyResourceType($methodName, $entityInstance, ResourceSet $resourceSet)
465
    {
466
        $resourceType = $resourceSet->getResourceType();
467
        $entityName = $resourceType->getInstanceType()->getName();
468
        if (!($entityInstance instanceof $entityName)) {
469
            throw ODataException::createInternalServerError(
470
                Messages::providersWrapperIDSQPMethodReturnsUnExpectedType(
471
                    $entityName,
472
                    $methodName
473
                )
474
            );
475
        }
476
477
        return $resourceType;
478
    }
479
}
480