Passed
Pull Request — master (#171)
by Alex
04:02
created

LaravelReadQuery::getResource()   B

Complexity

Conditions 10
Paths 32

Size

Total Lines 48
Code Lines 28

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 28
dl 0
loc 48
rs 7.6666
c 0
b 0
f 0
cc 10
nc 32
nop 5

How to fix   Complexity   

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:

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
    protected $auth;
26
27
    public function __construct(AuthInterface $auth = null)
28
    {
29
        $this->auth = isset($auth) ? $auth : new NullAuthProvider();
30
    }
31
32
    /**
33
     * Gets collection of entities belongs to an entity set
34
     * IE: http://host/EntitySet
35
     *  http://host/EntitySet?$skip=10&$top=5&filter=Prop gt Value.
36
     *
37
     * @param QueryType                $queryType            Is this is a query for a count, entities,
38
     *                                                       or entities-with-count?
39
     * @param ResourceSet              $resourceSet          The entity set containing the entities to fetch
40
     * @param FilterInfo|null          $filterInfo           The $filter parameter of the OData query.  NULL if absent
41
     * @param null|InternalOrderByInfo $orderBy              sorted order if we want to get the data in some
42
     *                                                       specific order
43
     * @param int|null                 $top                  number of records which need to be retrieved
44
     * @param int|null                 $skip                 number of records which need to be skipped
45
     * @param SkipTokenInfo|null       $skipToken            value indicating what records to skip
46
     * @param string[]|null            $eagerLoad            array of relations to eager load
47
     * @param Model|Relation|null      $sourceEntityInstance Starting point of query
48
     *
49
     * @return QueryResult
50
     */
51
    public function getResourceSet(
52
        QueryType $queryType,
53
        ResourceSet $resourceSet,
54
        $filterInfo = null,
55
        $orderBy = null,
56
        $top = null,
57
        $skip = null,
58
        $skipToken = null,
59
        array $eagerLoad = null,
60
        $sourceEntityInstance = null
61
    ) {
62
        if (null != $filterInfo && !($filterInfo instanceof FilterInfo)) {
0 ignored issues
show
introduced by
$filterInfo is always a sub-type of POData\UriProcessor\Quer...essionParser\FilterInfo.
Loading history...
63
            $msg = 'Filter info must be either null or instance of FilterInfo.';
64
            throw new InvalidArgumentException($msg);
65
        }
66
        if (null != $skipToken && !($skipToken instanceof SkipTokenInfo)) {
0 ignored issues
show
introduced by
$skipToken is always a sub-type of POData\UriProcessor\Quer...kenParser\SkipTokenInfo.
Loading history...
67
            $msg = 'Skip token must be either null or instance of SkipTokenInfo.';
68
            throw new InvalidArgumentException($msg);
69
        }
70
        $rawLoad = $this->processEagerLoadList($eagerLoad);
71
        $modelLoad = [];
72
73
        $this->checkSourceInstance($sourceEntityInstance);
74
        if (null == $sourceEntityInstance) {
75
            $sourceEntityInstance = $this->getSourceEntityInstance($resourceSet);
76
        }
77
78
        $keyName = null;
79
        $tableName = null;
80
        if ($sourceEntityInstance instanceof Model) {
81
            $modelLoad = $sourceEntityInstance->getEagerLoad();
82
            $keyName = $sourceEntityInstance->getKeyName();
83
            $tableName = $sourceEntityInstance->getTable();
84
        } elseif ($sourceEntityInstance instanceof Relation) {
85
            $modelLoad = $sourceEntityInstance->getRelated()->getEagerLoad();
86
            $keyName = $sourceEntityInstance->getRelated()->getKeyName();
87
            $tableName = $sourceEntityInstance->getRelated()->getTable();
88
        }
89
        if (null === $keyName) {
90
            throw new InvalidOperationException('Key name not retrieved');
91
        }
92
        $rawLoad = array_values(array_unique(array_merge($rawLoad, $modelLoad)));
93
94
        $checkInstance = $sourceEntityInstance instanceof Model ? $sourceEntityInstance : null;
95
        $this->checkAuth($sourceEntityInstance, $checkInstance);
96
97
        $result          = new QueryResult();
98
        $result->results = null;
99
        $result->count   = null;
100
101
        if (null != $orderBy) {
102
            foreach ($orderBy->getOrderByInfo()->getOrderByPathSegments() as $order) {
103
                foreach ($order->getSubPathSegments() as $subOrder) {
104
                    $subName = $subOrder->getName();
105
                    $subName = $tableName.'.'.$subName;
106
                    $sourceEntityInstance = $sourceEntityInstance->orderBy(
107
                        $subName,
108
                        $order->isAscending() ? 'asc' : 'desc'
109
                    );
110
                }
111
            }
112
        }
113
114
        // throttle up for trench run
115
        if (null != $skipToken) {
116
            $sourceEntityInstance = $this->processSkipToken($skipToken, $sourceEntityInstance, $keyName);
117
        }
118
119
        if (!isset($skip)) {
120
            $skip = 0;
121
        }
122
        if (!isset($top)) {
123
            $top = PHP_INT_MAX;
124
        }
125
126
        $nullFilter = true;
127
        $isvalid = null;
128
        if (isset($filterInfo)) {
129
            $method = 'return ' . $filterInfo->getExpressionAsString() . ';';
130
            $clln = '$' . $resourceSet->getResourceType()->getName();
131
            $isvalid = create_function($clln, $method);
0 ignored issues
show
Deprecated Code introduced by
The function create_function() has been deprecated: 7.2 ( Ignorable by Annotation )

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

131
            $isvalid = /** @scrutinizer ignore-deprecated */ create_function($clln, $method);

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
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...
132
            $nullFilter = false;
133
        }
134
135
        list($bulkSetCount, $resultSet, $resultCount, $skip) = $this->applyFiltering(
136
            $top,
137
            $skip,
138
            $sourceEntityInstance,
139
            $nullFilter,
140
            $rawLoad,
141
            $isvalid
142
        );
143
144
        if (isset($top)) {
145
            $resultSet = $resultSet->take($top);
146
        }
147
148
        if (QueryType::ENTITIES() == $queryType || QueryType::ENTITIES_WITH_COUNT() == $queryType) {
149
            $result->results = [];
150
            foreach ($resultSet as $res) {
151
                $result->results[] = $res;
152
            }
153
        }
154
        if (QueryType::COUNT() == $queryType || QueryType::ENTITIES_WITH_COUNT() == $queryType) {
155
            $result->count = $resultCount;
156
        }
157
        $hazMore = $bulkSetCount > $skip+count($resultSet);
158
        $result->hasMore = $hazMore;
159
        return $result;
160
    }
161
162
    /**
163
     * Get related resource set for a resource
164
     * IE: http://host/EntitySet(1L)/NavigationPropertyToCollection
165
     * http://host/EntitySet?$expand=NavigationPropertyToCollection.
166
     *
167
     * @param QueryType          $queryType            Is this is a query for a count, entities, or entities-with-count
168
     * @param ResourceSet        $sourceResourceSet    The entity set containing the source entity
169
     * @param Model              $sourceEntityInstance The source entity instance
170
     * @param ResourceSet        $targetResourceSet    The resource set pointed to by the navigation property
171
     * @param ResourceProperty   $targetProperty       The navigation property to retrieve
172
     * @param FilterInfo|null    $filter               The $filter parameter of the OData query.  NULL if none specified
173
     * @param mixed|null         $orderBy              sorted order if we want to get the data in some specific order
174
     * @param int|null           $top                  number of records which need to be retrieved
175
     * @param int|null           $skip                 number of records which need to be skipped
176
     * @param SkipTokenInfo|null $skipToken            value indicating what records to skip
177
     *
178
     * @return QueryResult
179
     */
180
    public function getRelatedResourceSet(
181
        QueryType $queryType,
182
        ResourceSet $sourceResourceSet,
183
        Model $sourceEntityInstance,
184
        ResourceSet $targetResourceSet,
185
        ResourceProperty $targetProperty,
186
        FilterInfo $filter = null,
187
        $orderBy = null,
188
        $top = null,
189
        $skip = null,
190
        SkipTokenInfo $skipToken = null
191
    ) {
192
        $this->checkAuth($sourceEntityInstance);
193
194
        $propertyName = $targetProperty->getName();
195
        $results = $sourceEntityInstance->$propertyName();
196
197
        return $this->getResourceSet(
198
            $queryType,
199
            $sourceResourceSet,
200
            $filter,
201
            $orderBy,
202
            $top,
203
            $skip,
204
            $skipToken,
205
            null,
206
            $results
207
        );
208
    }
209
210
    /**
211
     * Gets an entity instance from an entity set identified by a key
212
     * IE: http://host/EntitySet(1L)
213
     * http://host/EntitySet(KeyA=2L,KeyB='someValue').
214
     *
215
     * @param ResourceSet        $resourceSet   The entity set containing the entity to fetch
216
     * @param KeyDescriptor|null $keyDescriptor The key identifying the entity to fetch
217
     * @param string[]|null      $eagerLoad     array of relations to eager load
218
     *
219
     * @return Model|null Returns entity instance if found else null
220
     */
221
    public function getResourceFromResourceSet(
222
        ResourceSet $resourceSet,
223
        KeyDescriptor $keyDescriptor = null,
224
        array $eagerLoad = null
225
    ) {
226
        return $this->getResource($resourceSet, $keyDescriptor, [], $eagerLoad);
227
    }
228
229
230
    /**
231
     * Common method for getResourceFromRelatedResourceSet() and getResourceFromResourceSet().
232
     *
233
     * @param ResourceSet|null    $resourceSet
234
     * @param KeyDescriptor|null  $keyDescriptor
235
     * @param Model|Relation|null $sourceEntityInstance Starting point of query
236
     *                                                  $param array               $whereCondition
237
     * @param string[]|null       $eagerLoad            array of relations to eager load
238
     *
239
     * @return Model|null
240
     */
241
    public function getResource(
242
        ResourceSet $resourceSet = null,
243
        KeyDescriptor $keyDescriptor = null,
244
        array $whereCondition = [],
245
        array $eagerLoad = null,
246
        $sourceEntityInstance = null
247
    ) {
248
        if (null == $resourceSet && null == $sourceEntityInstance) {
249
            $msg = 'Must supply at least one of a resource set and source entity.';
250
            throw new \Exception($msg);
251
        }
252
253
        $this->checkSourceInstance($sourceEntityInstance);
254
        $rawLoad = $this->processEagerLoadList($eagerLoad);
255
256
        if (null == $sourceEntityInstance) {
257
            if (null == $resourceSet) {
258
                $msg = '';
259
                throw new InvalidOperationException($msg);
260
            }
261
            $sourceEntityInstance = $this->getSourceEntityInstance($resourceSet);
262
        }
263
264
        $this->checkAuth($sourceEntityInstance);
265
        if ($sourceEntityInstance instanceof Model) {
266
            $modelLoad = $sourceEntityInstance->getEagerLoad();
267
        } elseif ($sourceEntityInstance instanceof Relation) {
268
            $modelLoad = $sourceEntityInstance->getRelated()->getEagerLoad();
269
        }
270
        if (!(isset($modelLoad))) {
271
            $msg = '';
272
            throw new InvalidOperationException($msg);
273
        }
274
275
        $this->processKeyDescriptor($sourceEntityInstance, $keyDescriptor);
276
        foreach ($whereCondition as $fieldName => $fieldValue) {
277
            $sourceEntityInstance = $sourceEntityInstance->where($fieldName, $fieldValue);
278
        }
279
280
        $rawLoad = array_values(array_unique(array_merge($rawLoad, $modelLoad)));
0 ignored issues
show
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...
281
        $sourceEntityInstance = $sourceEntityInstance->get();
282
        $sourceCount = $sourceEntityInstance->count();
283
        if (0 == $sourceCount) {
284
            return null;
285
        }
286
        $result = $sourceEntityInstance->first();
287
288
        return $result;
289
    }
290
291
    /**
292
     * Get related resource for a resource
293
     * IE: http://host/EntitySet(1L)/NavigationPropertyToSingleEntity
294
     * http://host/EntitySet?$expand=NavigationPropertyToSingleEntity.
295
     *
296
     * @param ResourceSet      $sourceResourceSet    The entity set containing the source entity
297
     * @param Model            $sourceEntityInstance the source entity instance
298
     * @param ResourceSet      $targetResourceSet    The entity set containing the entity pointed to by the nav property
299
     * @param ResourceProperty $targetProperty       The navigation property to fetch
300
     *
301
     * @return Model|null The related resource if found else null
302
     */
303
    public function getRelatedResourceReference(
304
        ResourceSet $sourceResourceSet,
305
        Model $sourceEntityInstance,
306
        ResourceSet $targetResourceSet,
307
        ResourceProperty $targetProperty
308
    ) {
309
        $this->checkAuth($sourceEntityInstance);
310
311
        $propertyName = $targetProperty->getName();
312
        $propertyName = $this->getLaravelRelationName($propertyName);
313
        $result = $sourceEntityInstance->$propertyName()->first();
314
        if (null === $result) {
315
            return null;
316
        }
317
        if (!$result instanceof Model) {
318
            throw new InvalidOperationException('Model not retrieved from Eloquent relation');
319
        }
320
        if ($targetProperty->getResourceType()->getInstanceType()->getName() != get_class($result)) {
321
            return null;
322
        }
323
        return $result;
324
    }
325
326
    /**
327
     * Gets a related entity instance from an entity set identified by a key
328
     * IE: http://host/EntitySet(1L)/NavigationPropertyToCollection(33).
329
     *
330
     * @param ResourceSet      $sourceResourceSet    The entity set containing the source entity
331
     * @param Model            $sourceEntityInstance the source entity instance
332
     * @param ResourceSet      $targetResourceSet    The entity set containing the entity to fetch
333
     * @param ResourceProperty $targetProperty       the metadata of the target property
334
     * @param KeyDescriptor    $keyDescriptor        The key identifying the entity to fetch
335
     *
336
     * @return Model|null Returns entity instance if found else null
337
     */
338
    public function getResourceFromRelatedResourceSet(
339
        ResourceSet $sourceResourceSet,
340
        Model $sourceEntityInstance,
341
        ResourceSet $targetResourceSet,
342
        ResourceProperty $targetProperty,
343
        KeyDescriptor $keyDescriptor
344
    ) {
345
        $propertyName = $targetProperty->getName();
346
        if (!method_exists($sourceEntityInstance, $propertyName)) {
347
            $msg = 'Relation method, ' . $propertyName . ', does not exist on supplied entity.';
348
            throw new InvalidArgumentException($msg);
349
        }
350
        // take key descriptor and turn it into where clause here, rather than in getResource call
351
        $sourceEntityInstance = $sourceEntityInstance->$propertyName();
352
        $this->processKeyDescriptor($sourceEntityInstance, $keyDescriptor);
353
        $result = $this->getResource(null, null, [], [], $sourceEntityInstance);
354
        if (!(null == $result || $result instanceof Model)) {
0 ignored issues
show
introduced by
$result is always a sub-type of Illuminate\Database\Eloquent\Model.
Loading history...
355
            $msg = 'GetResourceFromRelatedResourceSet must return an entity or null';
356
            throw new InvalidOperationException($msg);
357
        }
358
        return $result;
359
    }
360
361
362
    /**
363
     * @param  ResourceSet $resourceSet
364
     * @return mixed
365
     */
366
    protected function getSourceEntityInstance(ResourceSet $resourceSet)
367
    {
368
        $entityClassName = $resourceSet->getResourceType()->getInstanceType()->name;
369
        return App::make($entityClassName);
370
    }
371
372
    /**
373
     * @param Model|Relation|null $source
374
     */
375
    protected function checkSourceInstance($source)
376
    {
377
        if (!(null == $source || $source instanceof Model || $source instanceof Relation)) {
0 ignored issues
show
introduced by
$source is always a sub-type of Illuminate\Database\Eloquent\Relations\Relation.
Loading history...
378
            $msg = 'Source entity instance must be null, a model, or a relation.';
379
            throw new InvalidArgumentException($msg);
380
        }
381
    }
382
383
    protected function getAuth()
384
    {
385
        return $this->auth;
386
    }
387
388
    /**
389
     * @param $sourceEntityInstance
390
     * @param null|mixed $checkInstance
391
     *
392
     * @throws ODataException
393
     */
394
    private function checkAuth($sourceEntityInstance, $checkInstance = null)
395
    {
396
        $check = $checkInstance instanceof Model ? $checkInstance
397
            : $checkInstance instanceof Relation ? $checkInstance
398
                : $sourceEntityInstance instanceof Model ? $sourceEntityInstance
399
                    : $sourceEntityInstance instanceof Relation ? $sourceEntityInstance
400
                        : null;
401
        if (!$this->getAuth()->canAuth(ActionVerb::READ(), get_class($sourceEntityInstance), $check)) {
402
            throw new ODataException('Access denied', 403);
403
        }
404
    }
405
406
    /**
407
     * @param $sourceEntityInstance
408
     * @param  KeyDescriptor|null        $keyDescriptor
409
     * @throws InvalidOperationException
410
     */
411
    private function processKeyDescriptor(&$sourceEntityInstance, KeyDescriptor $keyDescriptor = null)
412
    {
413
        if ($keyDescriptor) {
414
            $table = ($sourceEntityInstance instanceof Model) ? $sourceEntityInstance->getTable().'.' : '';
415
            foreach ($keyDescriptor->getValidatedNamedValues() as $key => $value) {
416
                $trimValue = trim($value[0], '\'');
417
                $sourceEntityInstance = $sourceEntityInstance->where($table.$key, $trimValue);
418
            }
419
        }
420
    }
421
422
    /**
423
     * @param  string[]|null $eagerLoad
424
     * @return array
425
     */
426
    private function processEagerLoadList(array $eagerLoad = null)
427
    {
428
        $load = (null === $eagerLoad) ? [] : $eagerLoad;
429
        $rawLoad = [];
430
        foreach ($load as $line) {
431
            if (!is_string($line)) {
432
                throw new InvalidOperationException('Eager-load elements must be non-empty strings');
433
            }
434
            $lineParts = explode('/', $line);
435
            $numberOfParts = count($lineParts);
436
            for ($i = 0; $i<$numberOfParts; $i++) {
437
                $lineParts[$i] = $this->getLaravelRelationName($lineParts[$i]);
438
            }
439
            $remixLine = implode('.', $lineParts);
440
            $rawLoad[] = $remixLine;
441
        }
442
        return $rawLoad;
443
    }
444
445
    /**
446
     * @param  string $odataProperty
447
     * @return string
448
     */
449
    private function getLaravelRelationName($odataProperty)
450
    {
451
        $laravelProperty = $odataProperty;
452
        $pos = strrpos($laravelProperty, '_');
453
        if ($pos !== false) {
454
            $laravelProperty = substr($laravelProperty, 0, $pos);
455
        }
456
        return $laravelProperty;
457
    }
458
459
    protected function processSkipToken($skipToken, $sourceEntityInstance, $keyName)
460
    {
461
        $parameters = [];
462
        $processed = [];
463
        $segments = $skipToken->getOrderByInfo()->getOrderByPathSegments();
464
        $values = $skipToken->getOrderByKeysInToken();
465
        $numValues = count($values);
466
        if ($numValues != count($segments)) {
467
            $msg = 'Expected '.count($segments).', got '.$numValues;
468
            throw new InvalidOperationException($msg);
469
        }
470
471
        for ($i = 0; $i < $numValues; $i++) {
472
            $relation = $segments[$i]->isAscending() ? '>' : '<';
473
            $name = $segments[$i]->getSubPathSegments()[0]->getName();
474
            $parameters[$name] = ['direction' => $relation, 'value' => trim($values[$i][0], '\'')];
475
        }
476
477
        foreach ($parameters as $name => $line) {
478
            $processed[$name] = ['direction' => $line['direction'], 'value' => $line['value']];
479
            $sourceEntityInstance = $sourceEntityInstance
480
                ->orWhere(
481
                    function ($query) use ($processed) {
482
                        foreach ($processed as $key => $proc) {
483
                            $query->where($key, $proc['direction'], $proc['value']);
484
                        }
485
                    }
486
                );
487
            // now we've handled the later-in-order segment for this key, drop it back to equality in prep
488
            // for next key - same-in-order for processed keys and later-in-order for next
489
            $processed[$name]['direction'] = '=';
490
        }
491
        return $sourceEntityInstance;
492
    }
493
494
    protected function applyFiltering($top, $skip, $sourceEntityInstance, $nullFilter, $rawLoad, $isvalid)
495
    {
496
        $bulkSetCount = $sourceEntityInstance->count();
497
        $bigSet = 20000 < $bulkSetCount;
498
499
        if ($nullFilter) {
500
            // default no-filter case, palm processing off to database engine - is a lot faster
501
            $resultSet = $sourceEntityInstance->skip($skip)->take($top)->with($rawLoad)->get();
502
            $resultCount = $bulkSetCount;
503
        } elseif ($bigSet) {
504
            if (!(isset($isvalid))) {
505
                $msg = 'Filter closure not set';
506
                throw new InvalidOperationException($msg);
507
            }
508
            $resultSet = collect([]);
509
            $rawCount = 0;
510
            $rawTop = null === $top ? $bulkSetCount : $top;
511
512
            // loop thru, chunk by chunk, to reduce chances of exhausting memory
513
            $sourceEntityInstance->chunk(
514
                5000,
515
                function ($results) use ($isvalid, &$skip, &$resultSet, &$rawCount, $rawTop) {
516
                    // apply filter
517
                    $results = $results->filter($isvalid);
518
                    // need to iterate through full result set to find count of items matching filter,
519
                    // so we can't bail out early
520
                    $rawCount += $results->count();
521
                    // now bolt on filtrate to accumulating result set if we haven't accumulated enough bitz
522
                    if ($rawTop > $resultSet->count() + $skip) {
523
                        $resultSet = collect(array_merge($resultSet->all(), $results->all()));
524
                        $sliceAmount = min($skip, $resultSet->count());
525
                        $resultSet = $resultSet->slice($sliceAmount);
526
                        $skip -= $sliceAmount;
527
                    }
528
                }
529
            );
530
531
            // clean up residual to-be-skipped records
532
            $resultSet = $resultSet->slice($skip);
533
            $resultCount = $rawCount;
534
        } else {
535
            if ($sourceEntityInstance instanceof Model) {
536
                $sourceEntityInstance = $sourceEntityInstance->getQuery();
537
            }
538
            $resultSet = $sourceEntityInstance->with($rawLoad)->get();
539
            $resultSet = $resultSet->filter($isvalid);
540
            $resultCount = $resultSet->count();
541
542
            if (isset($skip)) {
543
                $resultSet = $resultSet->slice($skip);
544
            }
545
        }
546
        return [$bulkSetCount, $resultSet, $resultCount, $skip];
547
    }
548
}
549