Passed
Pull Request — master (#154)
by Alex
07:05
created

LaravelReadQuery::getResourceSet()   F

Complexity

Conditions 35
Paths > 20000

Size

Total Lines 170
Code Lines 105

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 170
rs 2
cc 35
eloc 105
nc 21506
nop 9

How to fix   Long Method    Complexity    Many Parameters   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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 AlgoWeb\PODataLaravel\Query;
4
5
use AlgoWeb\PODataLaravel\Auth\NullAuthProvider;
6
use AlgoWeb\PODataLaravel\Enums\ActionVerb;
7
use AlgoWeb\PODataLaravel\Interfaces\AuthInterface;
8
use Illuminate\Database\Eloquent\Model;
9
use Illuminate\Database\Eloquent\Relations\Relation;
10
use Illuminate\Support\Facades\App;
11
use POData\Common\InvalidOperationException;
12
use POData\Common\ODataException;
13
use POData\Providers\Metadata\ResourceProperty;
14
use POData\Providers\Metadata\ResourceSet;
15
use POData\Providers\Query\QueryResult;
16
use POData\Providers\Query\QueryType;
17
use POData\UriProcessor\QueryProcessor\ExpressionParser\FilterInfo;
18
use POData\UriProcessor\QueryProcessor\OrderByParser\InternalOrderByInfo;
19
use POData\UriProcessor\QueryProcessor\SkipTokenParser\SkipTokenInfo;
20
use POData\UriProcessor\ResourcePathProcessor\SegmentParser\KeyDescriptor;
21
use Symfony\Component\Process\Exception\InvalidArgumentException;
22
23
class LaravelReadQuery
24
{
25
    const PK = 'PrimaryKey';
26
27
    protected $auth;
28
29
    public function __construct(AuthInterface $auth = null)
30
    {
31
        $this->auth = isset($auth) ? $auth : new NullAuthProvider();
32
    }
33
34
    /**
35
     * Gets collection of entities belongs to an entity set
36
     * IE: http://host/EntitySet
37
     *  http://host/EntitySet?$skip=10&$top=5&filter=Prop gt Value.
38
     *
39
     * @param QueryType                $queryType            Is this is a query for a count, entities,
40
     *                                                       or entities-with-count?
41
     * @param ResourceSet              $resourceSet          The entity set containing the entities to fetch
42
     * @param FilterInfo|null          $filterInfo           The $filter parameter of the OData query.  NULL if absent
43
     * @param null|InternalOrderByInfo $orderBy              sorted order if we want to get the data in some
44
     *                                                       specific order
45
     * @param int|null                 $top                  number of records which need to be retrieved
46
     * @param int|null                 $skip                 number of records which need to be skipped
47
     * @param SkipTokenInfo|null       $skipToken            value indicating what records to skip
48
     * @param string[]|null            $eagerLoad            array of relations to eager load
49
     * @param Model|Relation|null      $sourceEntityInstance Starting point of query
50
     *
51
     * @return QueryResult
52
     */
53
    public function getResourceSet(
54
        QueryType $queryType,
55
        ResourceSet $resourceSet,
56
        $filterInfo = null,
57
        $orderBy = null,
58
        $top = null,
59
        $skip = null,
60
        $skipToken = null,
61
        array $eagerLoad = null,
62
        $sourceEntityInstance = null
63
    ) {
64
        if (null != $filterInfo && !($filterInfo instanceof FilterInfo)) {
65
            $msg = 'Filter info must be either null or instance of FilterInfo.';
66
            throw new InvalidArgumentException($msg);
67
        }
68
        if (null != $skipToken && !($skipToken instanceof SkipTokenInfo)) {
69
            $msg = 'Skip token must be either null or instance of SkipTokenInfo.';
70
            throw new InvalidArgumentException($msg);
71
        }
72
        $rawLoad = $this->processEagerLoadList($eagerLoad);
73
        $modelLoad = [];
74
75
        $this->checkSourceInstance($sourceEntityInstance);
76
        if (null == $sourceEntityInstance) {
77
            $sourceEntityInstance = $this->getSourceEntityInstance($resourceSet);
78
        }
79
80
        $keyName = null;
81
        if ($sourceEntityInstance instanceof Model) {
82
            $modelLoad = $sourceEntityInstance->getEagerLoad();
83
            $keyName = $sourceEntityInstance->getKeyName();
84
        } elseif ($sourceEntityInstance instanceof Relation) {
85
            $modelLoad = $sourceEntityInstance->getRelated()->getEagerLoad();
86
            $keyName = $sourceEntityInstance->getRelated()->getKeyName();
87
        }
88
        assert(isset($keyName));
0 ignored issues
show
Bug introduced by
The call to assert() has too few arguments starting with description. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

88
        /** @scrutinizer ignore-call */ 
89
        assert(isset($keyName));

This check compares calls to functions or methods with their respective definitions. If the call has less arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
89
        $rawLoad = array_values(array_unique(array_merge($rawLoad, $modelLoad)));
0 ignored issues
show
Bug introduced by
It seems like $modelLoad can also be of type Illuminate\Database\Eloquent\Builder; however, parameter $array2 of array_merge() does only seem to accept null|array, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

89
        $rawLoad = array_values(array_unique(array_merge($rawLoad, /** @scrutinizer ignore-type */ $modelLoad)));
Loading history...
90
91
        $checkInstance = $sourceEntityInstance instanceof Model ? $sourceEntityInstance : null;
92
        $this->checkAuth($sourceEntityInstance, $checkInstance);
93
94
        $result          = new QueryResult();
95
        $result->results = null;
96
        $result->count   = null;
97
98
        if (null != $orderBy) {
99
            foreach ($orderBy->getOrderByInfo()->getOrderByPathSegments() as $order) {
100
                foreach ($order->getSubPathSegments() as $subOrder) {
101
                    $subName = $subOrder->getName();
102
                    $subName = (self::PK == $subName) ? $keyName : $subName;
103
                    $sourceEntityInstance = $sourceEntityInstance->orderBy(
104
                        $subName,
105
                        $order->isAscending() ? 'asc' : 'desc'
106
                    );
107
                }
108
            }
109
        }
110
111
        // throttle up for trench run
112
        if (null != $skipToken) {
113
            $parameters = [];
114
            $processed = [];
115
            $segments = $skipToken->getOrderByInfo()->getOrderByPathSegments();
116
            $values = $skipToken->getOrderByKeysInToken();
117
            $numValues = count($values);
118
            assert($numValues == count($segments));
119
120
            for ($i = 0; $i < $numValues; $i++) {
121
                $relation = $segments[$i]->isAscending() ? '>' : '<';
122
                $name = $segments[$i]->getSubPathSegments()[0]->getName();
123
                $name = (self::PK == $name) ? $keyName : $name;
124
                $parameters[$name] = ['direction' => $relation, 'value' => trim($values[$i][0], '\'')];
125
            }
126
127
            foreach ($parameters as $name => $line) {
128
                $processed[$name] = ['direction' => $line['direction'], 'value' => $line['value']];
129
                $sourceEntityInstance = $sourceEntityInstance
130
                    ->orWhere(
131
                        function ($query) use ($processed) {
132
                            foreach ($processed as $key => $proc) {
133
                                $query->where($key, $proc['direction'], $proc['value']);
134
                            }
135
                        }
136
                    );
137
                // now we've handled the later-in-order segment for this key, drop it back to equality in prep
138
                // for next key - same-in-order for processed keys and later-in-order for next
139
                $processed[$name]['direction'] = '=';
140
            }
141
        }
142
143
        if (!isset($skip)) {
144
            $skip = 0;
145
        }
146
        if (!isset($top)) {
147
            $top = PHP_INT_MAX;
148
        }
149
150
        $nullFilter = true;
151
        $isvalid = null;
152
        if (isset($filterInfo)) {
153
            $method = 'return ' . $filterInfo->getExpressionAsString() . ';';
154
            $clln = '$' . $resourceSet->getResourceType()->getName();
155
            $isvalid = create_function($clln, $method);
0 ignored issues
show
Security Best Practice introduced by
The use of create_function is highly discouraged, better use a closure.

create_function can pose a great security vulnerability as it is similar to eval, and could be used for arbitrary code execution. We highly recommend to use a closure instead.

// Instead of
$function = create_function('$a, $b', 'return $a + $b');

// Better use
$function = function($a, $b) { return $a + $b; }
Loading history...
156
            $nullFilter = false;
157
        }
158
159
        $bulkSetCount = $sourceEntityInstance->count();
160
        $bigSet = 20000 < $bulkSetCount;
161
162
        if ($nullFilter) {
163
            // default no-filter case, palm processing off to database engine - is a lot faster
164
            $resultSet = $sourceEntityInstance->skip($skip)->take($top)->with($rawLoad)->get();
165
            $resultCount = $bulkSetCount;
166
        } elseif ($bigSet) {
167
            assert(isset($isvalid), 'Filter closure not set');
168
            $resultSet = collect([]);
169
            $rawCount = 0;
170
            $rawTop = null === $top ? $bulkSetCount : $top;
171
172
            // loop thru, chunk by chunk, to reduce chances of exhausting memory
173
            $sourceEntityInstance->chunk(
174
                5000,
175
                function ($results) use ($isvalid, &$skip, &$resultSet, &$rawCount, $rawTop) {
176
                    // apply filter
177
                    $results = $results->filter($isvalid);
178
                    // need to iterate through full result set to find count of items matching filter,
179
                    // so we can't bail out early
180
                    $rawCount += $results->count();
181
                    // now bolt on filtrate to accumulating result set if we haven't accumulated enough bitz
182
                    if ($rawTop > $resultSet->count()+$skip) {
183
                        $resultSet = collect(array_merge($resultSet->all(), $results->all()));
184
                        $sliceAmount = min($skip, $resultSet->count());
185
                        $resultSet = $resultSet->slice($sliceAmount);
186
                        $skip -= $sliceAmount;
187
                    }
188
                }
189
            );
190
191
            // clean up residual to-be-skipped records
192
            $resultSet = $resultSet->slice($skip);
193
            $resultCount = $rawCount;
194
        } else {
195
            if ($sourceEntityInstance instanceof Model) {
196
                $sourceEntityInstance = $sourceEntityInstance->getQuery();
197
            }
198
            $resultSet = $sourceEntityInstance->with($rawLoad)->get();
199
            $resultSet = $resultSet->filter($isvalid);
200
            $resultCount = $resultSet->count();
201
202
            if (isset($skip)) {
203
                $resultSet = $resultSet->slice($skip);
204
            }
205
        }
206
207
        if (isset($top)) {
208
            $resultSet = $resultSet->take($top);
209
        }
210
211
        if (QueryType::ENTITIES() == $queryType || QueryType::ENTITIES_WITH_COUNT() == $queryType) {
212
            $result->results = [];
213
            foreach ($resultSet as $res) {
214
                $result->results[] = $res;
215
            }
216
        }
217
        if (QueryType::COUNT() == $queryType || QueryType::ENTITIES_WITH_COUNT() == $queryType) {
218
            $result->count = $resultCount;
0 ignored issues
show
Documentation Bug introduced by
It seems like $resultCount can also be of type Illuminate\Database\Query\Builder or Illuminate\Database\Eloq...Database\Eloquent\Model or Illuminate\Database\Eloquent\Model or Illuminate\Database\Eloquent\Builder or Illuminate\Database\Eloquent\Relations\Relation. However, the property $count is declared as type null|integer. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
219
        }
220
        $hazMore = $bulkSetCount > $skip+count($resultSet);
221
        $result->hasMore = $hazMore;
222
        return $result;
223
    }
224
225
    /**
226
     * Get related resource set for a resource
227
     * IE: http://host/EntitySet(1L)/NavigationPropertyToCollection
228
     * http://host/EntitySet?$expand=NavigationPropertyToCollection.
229
     *
230
     * @param QueryType          $queryType            Is this is a query for a count, entities, or entities-with-count
231
     * @param ResourceSet        $sourceResourceSet    The entity set containing the source entity
232
     * @param Model              $sourceEntityInstance The source entity instance
233
     * @param ResourceSet        $targetResourceSet    The resource set pointed to by the navigation property
234
     * @param ResourceProperty   $targetProperty       The navigation property to retrieve
235
     * @param FilterInfo|null    $filter               The $filter parameter of the OData query.  NULL if none specified
236
     * @param mixed|null         $orderBy              sorted order if we want to get the data in some specific order
237
     * @param int|null           $top                  number of records which need to be retrieved
238
     * @param int|null           $skip                 number of records which need to be skipped
239
     * @param SkipTokenInfo|null $skipToken            value indicating what records to skip
240
     *
241
     * @return QueryResult
242
     */
243
    public function getRelatedResourceSet(
244
        QueryType $queryType,
245
        ResourceSet $sourceResourceSet,
246
        Model $sourceEntityInstance,
247
        ResourceSet $targetResourceSet,
248
        ResourceProperty $targetProperty,
249
        FilterInfo $filter = null,
250
        $orderBy = null,
251
        $top = null,
252
        $skip = null,
253
        SkipTokenInfo $skipToken = null
254
    ) {
255
        $this->checkAuth($sourceEntityInstance);
256
257
        $propertyName = $targetProperty->getName();
258
        $results = $sourceEntityInstance->$propertyName();
259
260
        return $this->getResourceSet(
261
            $queryType,
262
            $sourceResourceSet,
263
            $filter,
264
            $orderBy,
265
            $top,
266
            $skip,
267
            $skipToken,
268
            null,
269
            $results
270
        );
271
    }
272
273
    /**
274
     * Gets an entity instance from an entity set identified by a key
275
     * IE: http://host/EntitySet(1L)
276
     * http://host/EntitySet(KeyA=2L,KeyB='someValue').
277
     *
278
     * @param ResourceSet        $resourceSet   The entity set containing the entity to fetch
279
     * @param KeyDescriptor|null $keyDescriptor The key identifying the entity to fetch
280
     * @param string[]|null      $eagerLoad     array of relations to eager load
281
     *
282
     * @return Model|null Returns entity instance if found else null
283
     */
284
    public function getResourceFromResourceSet(
285
        ResourceSet $resourceSet,
286
        KeyDescriptor $keyDescriptor = null,
287
        array $eagerLoad = null
288
    ) {
289
        return $this->getResource($resourceSet, $keyDescriptor, [], $eagerLoad);
290
    }
291
292
293
    /**
294
     * Common method for getResourceFromRelatedResourceSet() and getResourceFromResourceSet().
295
     *
296
     * @param ResourceSet|null    $resourceSet
297
     * @param KeyDescriptor|null  $keyDescriptor
298
     * @param Model|Relation|null $sourceEntityInstance Starting point of query
299
     *                                                  $param array               $whereCondition
300
     * @param string[]|null       $eagerLoad            array of relations to eager load
301
     *
302
     * @return Model|null
303
     */
304
    public function getResource(
305
        ResourceSet $resourceSet = null,
306
        KeyDescriptor $keyDescriptor = null,
307
        array $whereCondition = [],
308
        array $eagerLoad = null,
309
        $sourceEntityInstance = null
310
    ) {
311
        if (null == $resourceSet && null == $sourceEntityInstance) {
312
            $msg = 'Must supply at least one of a resource set and source entity.';
313
            throw new \Exception($msg);
314
        }
315
316
        $this->checkSourceInstance($sourceEntityInstance);
317
        $rawLoad = $this->processEagerLoadList($eagerLoad);
318
319
        if (null == $sourceEntityInstance) {
320
            assert(null != $resourceSet);
0 ignored issues
show
Bug introduced by
The call to assert() has too few arguments starting with description. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

320
            /** @scrutinizer ignore-call */ 
321
            assert(null != $resourceSet);

This check compares calls to functions or methods with their respective definitions. If the call has less arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
321
            $sourceEntityInstance = $this->getSourceEntityInstance($resourceSet);
0 ignored issues
show
Bug introduced by
It seems like $resourceSet can also be of type null; however, parameter $resourceSet of AlgoWeb\PODataLaravel\Qu...tSourceEntityInstance() does only seem to accept POData\Providers\Metadata\ResourceSet, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

321
            $sourceEntityInstance = $this->getSourceEntityInstance(/** @scrutinizer ignore-type */ $resourceSet);
Loading history...
322
        }
323
324
        $this->checkAuth($sourceEntityInstance);
325
        if ($sourceEntityInstance instanceof Model) {
326
            $modelLoad = $sourceEntityInstance->getEagerLoad();
327
        } elseif ($sourceEntityInstance instanceof Relation) {
328
            $modelLoad = $sourceEntityInstance->getRelated()->getEagerLoad();
329
        }
330
        assert(isset($modelLoad));
331
332
        $this->processKeyDescriptor($sourceEntityInstance, $keyDescriptor);
333
        foreach ($whereCondition as $fieldName => $fieldValue) {
334
            $sourceEntityInstance = $sourceEntityInstance->where($fieldName, $fieldValue);
335
        }
336
337
        $rawLoad = array_values(array_unique(array_merge($rawLoad, $modelLoad)));
0 ignored issues
show
Bug introduced by
It seems like $modelLoad can also be of type Illuminate\Database\Eloquent\Builder; however, parameter $array2 of array_merge() does only seem to accept null|array, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

337
        $rawLoad = array_values(array_unique(array_merge($rawLoad, /** @scrutinizer ignore-type */ $modelLoad)));
Loading history...
Comprehensibility Best Practice introduced by
The variable $modelLoad does not seem to be defined for all execution paths leading up to this point.
Loading history...
Unused Code introduced by
The assignment to $rawLoad is dead and can be removed.
Loading history...
338
        $sourceEntityInstance = $sourceEntityInstance->get();
339
        $sourceCount = $sourceEntityInstance->count();
340
        if (0 == $sourceCount) {
341
            return null;
342
        }
343
        $result = $sourceEntityInstance->first();
344
        $result->PrimaryKey = $result->getKey();
345
346
        return $result;
347
    }
348
349
    /**
350
     * Get related resource for a resource
351
     * IE: http://host/EntitySet(1L)/NavigationPropertyToSingleEntity
352
     * http://host/EntitySet?$expand=NavigationPropertyToSingleEntity.
353
     *
354
     * @param ResourceSet      $sourceResourceSet    The entity set containing the source entity
355
     * @param Model            $sourceEntityInstance the source entity instance
356
     * @param ResourceSet      $targetResourceSet    The entity set containing the entity pointed to by the nav property
357
     * @param ResourceProperty $targetProperty       The navigation property to fetch
358
     *
359
     * @return object|null The related resource if found else null
360
     */
361
    public function getRelatedResourceReference(
362
        ResourceSet $sourceResourceSet,
363
        Model $sourceEntityInstance,
364
        ResourceSet $targetResourceSet,
365
        ResourceProperty $targetProperty
366
    ) {
367
        $this->checkAuth($sourceEntityInstance);
368
369
        $propertyName = $targetProperty->getName();
370
        $propertyName = $this->getLaravelRelationName($propertyName);
371
        $result = $sourceEntityInstance->$propertyName;
372
        if (null === $result) {
373
            return null;
374
        }
375
        assert($result instanceof Model, get_class($result));
376
        if ($targetProperty->getResourceType()->getInstanceType()->getName() != get_class($result)) {
377
            return null;
378
        }
379
        return $result;
380
    }
381
382
    /**
383
     * Gets a related entity instance from an entity set identified by a key
384
     * IE: http://host/EntitySet(1L)/NavigationPropertyToCollection(33).
385
     *
386
     * @param ResourceSet      $sourceResourceSet    The entity set containing the source entity
387
     * @param Model            $sourceEntityInstance the source entity instance
388
     * @param ResourceSet      $targetResourceSet    The entity set containing the entity to fetch
389
     * @param ResourceProperty $targetProperty       the metadata of the target property
390
     * @param KeyDescriptor    $keyDescriptor        The key identifying the entity to fetch
391
     *
392
     * @return Model|null Returns entity instance if found else null
393
     */
394
    public function getResourceFromRelatedResourceSet(
395
        ResourceSet $sourceResourceSet,
396
        Model $sourceEntityInstance,
397
        ResourceSet $targetResourceSet,
398
        ResourceProperty $targetProperty,
399
        KeyDescriptor $keyDescriptor
400
    ) {
401
        $propertyName = $targetProperty->getName();
402
        if (!method_exists($sourceEntityInstance, $propertyName)) {
403
            $msg = 'Relation method, ' . $propertyName . ', does not exist on supplied entity.';
404
            throw new InvalidArgumentException($msg);
405
        }
406
        // take key descriptor and turn it into where clause here, rather than in getResource call
407
        $sourceEntityInstance = $sourceEntityInstance->$propertyName();
408
        $this->processKeyDescriptor($sourceEntityInstance, $keyDescriptor);
409
        $result = $this->getResource(null, null, [], [], $sourceEntityInstance);
410
        assert(
411
            $result instanceof Model || null == $result,
412
            'GetResourceFromRelatedResourceSet must return an entity or null'
413
        );
414
        return $result;
415
    }
416
417
418
    /**
419
     * @param  ResourceSet $resourceSet
420
     * @return mixed
421
     */
422
    protected function getSourceEntityInstance(ResourceSet $resourceSet)
423
    {
424
        $entityClassName = $resourceSet->getResourceType()->getInstanceType()->name;
425
        return App::make($entityClassName);
426
    }
427
428
    /**
429
     * @param Model|Relation|null $source
430
     */
431
    protected function checkSourceInstance($source)
432
    {
433
        if (!(null == $source || $source instanceof Model || $source instanceof Relation)) {
434
            $msg = 'Source entity instance must be null, a model, or a relation.';
435
            throw new InvalidArgumentException($msg);
436
        }
437
    }
438
439
    protected function getAuth()
440
    {
441
        return $this->auth;
442
    }
443
444
    /**
445
     * @param $sourceEntityInstance
446
     * @param null|mixed $checkInstance
447
     *
448
     * @throws ODataException
449
     */
450
    private function checkAuth($sourceEntityInstance, $checkInstance = null)
451
    {
452
        $check = $checkInstance instanceof Model ? $checkInstance
453
            : $checkInstance instanceof Relation ? $checkInstance
454
                : $sourceEntityInstance instanceof Model ? $sourceEntityInstance
455
                    : $sourceEntityInstance instanceof Relation ? $sourceEntityInstance
456
                        : null;
457
        if (!$this->getAuth()->canAuth(ActionVerb::READ(), get_class($sourceEntityInstance), $check)) {
0 ignored issues
show
Bug introduced by
The method READ() does not exist on AlgoWeb\PODataLaravel\Enums\ActionVerb. Since you implemented __callStatic, consider adding a @method annotation. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

457
        if (!$this->getAuth()->canAuth(ActionVerb::/** @scrutinizer ignore-call */ READ(), get_class($sourceEntityInstance), $check)) {
Loading history...
458
            throw new ODataException('Access denied', 403);
459
        }
460
    }
461
462
    /**
463
     * @param $sourceEntityInstance
464
     * @param  KeyDescriptor|null        $keyDescriptor
465
     * @throws InvalidOperationException
466
     */
467
    private function processKeyDescriptor(&$sourceEntityInstance, KeyDescriptor $keyDescriptor = null)
468
    {
469
        if ($keyDescriptor) {
470
            foreach ($keyDescriptor->getValidatedNamedValues() as $key => $value) {
471
                $key = (self::PK == $key) ? $sourceEntityInstance->getKeyName() : $key;
472
                $trimValue = trim($value[0], '\'');
473
                $sourceEntityInstance = $sourceEntityInstance->where($key, $trimValue);
474
            }
475
        }
476
    }
477
478
    /**
479
     * @param  string[]|null $eagerLoad
480
     * @return array
481
     */
482
    private function processEagerLoadList(array $eagerLoad = null)
483
    {
484
        $load = (null === $eagerLoad) ? [] : $eagerLoad;
485
        $rawLoad = [];
486
        foreach ($load as $line) {
487
            assert(is_string($line), 'Eager-load elements must be non-empty strings');
488
            $lineParts = explode('/', $line);
489
            $numberOfParts = count($lineParts);
490
            for ($i = 0; $i<$numberOfParts; $i++) {
491
                $lineParts[$i] = $this->getLaravelRelationName($lineParts[$i]);
492
            }
493
            $remixLine = implode('.', $lineParts);
494
            $rawLoad[] = $remixLine;
495
        }
496
        return $rawLoad;
497
    }
498
499
    /**
500
     * @param  string $odataProperty
501
     * @return string
502
     */
503
    private function getLaravelRelationName($odataProperty)
504
    {
505
        $laravelProperty = $odataProperty;
506
        $pos = strrpos($laravelProperty, '_');
507
        if ($pos !== false) {
508
            $laravelProperty = substr($laravelProperty, 0, $pos);
509
        }
510
        return $laravelProperty;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $laravelProperty could also return false which is incompatible with the documented return type string. Did you maybe forget to handle an error condition?

If the returned type also contains false, it is an indicator that maybe an error condition leading to the specific return statement remains unhandled.

Loading history...
511
    }
512
}
513