Passed
Pull Request — master (#182)
by Alex
05:02
created

LaravelQuery::getRelatedResourceSet()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 24
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 1

Importance

Changes 0
Metric Value
eloc 12
c 0
b 0
f 0
dl 0
loc 24
rs 9.8666
ccs 5
cts 5
cp 1
cc 1
nc 1
nop 10
crap 1

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 AlgoWeb\PODataLaravel\Query;
4
5
use AlgoWeb\PODataLaravel\Auth\NullAuthProvider;
6
use AlgoWeb\PODataLaravel\Controllers\MetadataControllerContainer;
7
use AlgoWeb\PODataLaravel\Enums\ActionVerb;
8
use AlgoWeb\PODataLaravel\Interfaces\AuthInterface;
9
use AlgoWeb\PODataLaravel\Providers\MetadataProvider;
10
use Illuminate\Database\Eloquent\Model;
11
use Illuminate\Database\Eloquent\Relations\Relation;
12
use Illuminate\Support\Facades\App;
13
use Illuminate\Support\Facades\DB;
14
use POData\Common\InvalidOperationException;
15
use POData\Common\ODataException;
16
use POData\Providers\Metadata\ResourceProperty;
17
use POData\Providers\Metadata\ResourceSet;
18
use POData\Providers\Query\IQueryProvider;
19
use POData\Providers\Query\QueryResult;
20
use POData\Providers\Query\QueryType;
21
use POData\UriProcessor\QueryProcessor\ExpressionParser\FilterInfo;
22
use POData\UriProcessor\QueryProcessor\OrderByParser\InternalOrderByInfo;
23
use POData\UriProcessor\QueryProcessor\SkipTokenParser\SkipTokenInfo;
24
use POData\UriProcessor\ResourcePathProcessor\SegmentParser\KeyDescriptor;
25
use Symfony\Component\Process\Exception\InvalidArgumentException;
26 5
27
class LaravelQuery extends LaravelBaseQuery implements IQueryProvider
28
{
29 5
    protected $expression;
30 5
    protected $reader;
31 5
    protected $modelHook;
32 5
    protected $bulk;
33
    protected $writer;
34
    public $queryProviderClassName;
35
    private static $touchList = [];
36
    private static $inBatch;
37
38
    public function __construct(AuthInterface $auth = null)
39
    {
40
        parent::__construct($auth);
41
        /* MySQLExpressionProvider();*/
42
        $this->expression = new LaravelExpressionProvider(); //PHPExpressionProvider('expression');
43
        $this->queryProviderClassName = get_class($this);
44
        $this->reader = new LaravelReadQuery($this->getAuth());
45
        $this->modelHook = new LaravelHookQuery($this->getAuth());
46
        $this->bulk = new LaravelBulkQuery($this, $this->getAuth());
47
        $this->writer = new LaravelWriteQuery($this->getAuth());
48
49
        self::$touchList = [];
50
        self::$inBatch = false;
51
    }
52
53
    /**
54
     * Indicates if the QueryProvider can handle ordered paging, this means respecting order, skip, and top parameters
55
     * If the query provider can not handle ordered paging, it must return the entire result set and POData will
56
     * perform the ordering and paging.
57
     *
58
     * @return Boolean True if the query provider can handle ordered paging, false if POData should perform the paging
59
     */
60
    public function handlesOrderedPaging()
61
    {
62
        return true;
63
    }
64
65
    /**
66
     * Gets the expression provider used by to compile OData expressions into expression used by this query provider.
67
     *
68
     * @return \POData\Providers\Expression\IExpressionProvider
69
     */
70 3
    public function getExpressionProvider()
71
    {
72
        return $this->expression;
73
    }
74
75
    /**
76
     * Gets the LaravelReadQuery instance used to handle read queries (repetitious, nyet?).
77
     *
78
     * @return LaravelReadQuery
79 3
     */
80
    public function getReader()
81
    {
82 3
        return $this->reader;
83 1
    }
84 1
85
    /**
86 3
     * Gets the LaravelHookQuery instance used to handle hook/unhook queries (repetitious, nyet?).
87 3
     *
88 3
     * @return LaravelHookQuery
89
     */
90 3
    public function getModelHook()
91
    {
92
        return $this->modelHook;
93
    }
94
95
    /**
96
     * Gets the LaravelBulkQuery instance used to handle bulk queries (repetitious, nyet?).
97 1
     *
98
     * @return LaravelBulkQuery
99
     */
100 3
    public function getBulk()
101 1
    {
102 1
        return $this->bulk;
103 3
    }
104 1
105 1
    /**
106
     * Gets collection of entities belongs to an entity set
107 3
     * IE: http://host/EntitySet
108
     *  http://host/EntitySet?$skip=10&$top=5&filter=Prop gt Value.
109 3
     *
110
     * @param QueryType                $queryType            Is this is a query for a count, entities,
111
     *                                                       or entities-with-count?
112
     * @param ResourceSet              $resourceSet          The entity set containing the entities to fetch
113
     * @param FilterInfo|null          $filterInfo           The $filter parameter of the OData query.  NULL if absent
114
     * @param null|InternalOrderByInfo $orderBy              sorted order if we want to get the data in some
115
     *                                                       specific order
116
     * @param int|null                 $top                  number of records which need to be retrieved
117 3
     * @param int|null                 $skip                 number of records which need to be skipped
118 1
     * @param SkipTokenInfo|null       $skipToken            value indicating what records to skip
119 1
     * @param string[]|null            $eagerLoad            array of relations to eager load
120 1
     * @param Model|Relation|null      $sourceEntityInstance Starting point of query
121 1
     *
122 1
     * @return QueryResult
123 3
     * @throws InvalidOperationException
124 3
     * @throws ODataException
125
     * @throws \ReflectionException
126
     */
127 3
    public function getResourceSet(
128 3
        QueryType $queryType,
129 3
        ResourceSet $resourceSet,
130
        $filterInfo = null,
131
        $orderBy = null,
132
        $top = null,
133
        $skip = null,
134
        $skipToken = null,
135
        array $eagerLoad = null,
136
        $sourceEntityInstance = null
137
    ) {
138
        /** @var Model|Relation|null $source */
139
        $source = $this->unpackSourceEntity($sourceEntityInstance);
140
        return $this->getReader()->getResourceSet(
141
            $queryType,
142
            $resourceSet,
143
            $filterInfo,
144
            $orderBy,
145
            $top,
146
            $skip,
147
            $skipToken,
148
            $eagerLoad,
149
            $source
150
        );
151
    }
152
    /**
153
     * Gets an entity instance from an entity set identified by a key
154
     * IE: http://host/EntitySet(1L)
155
     * http://host/EntitySet(KeyA=2L,KeyB='someValue').
156
     *
157
     * @param ResourceSet        $resourceSet   The entity set containing the entity to fetch
158
     * @param KeyDescriptor|null $keyDescriptor The key identifying the entity to fetch
159
     * @param string[]|null      $eagerLoad     array of relations to eager load
160
     *
161
     * @return Model|null Returns entity instance if found else null
162
     * @throws \Exception
163
     */
164
    public function getResourceFromResourceSet(
165
        ResourceSet $resourceSet,
166
        KeyDescriptor $keyDescriptor = null,
167
        array $eagerLoad = null
168
    ) {
169
        return $this->getReader()->getResourceFromResourceSet($resourceSet, $keyDescriptor, $eagerLoad);
170
    }
171
172
    /**
173
     * Get related resource set for a resource
174
     * IE: http://host/EntitySet(1L)/NavigationPropertyToCollection
175
     * http://host/EntitySet?$expand=NavigationPropertyToCollection.
176
     *
177
     * @param QueryType          $queryType            Is this is a query for a count, entities, or entities-with-count
178
     * @param ResourceSet        $sourceResourceSet    The entity set containing the source entity
179
     * @param object             $sourceEntityInstance The source entity instance
180
     * @param ResourceSet        $targetResourceSet    The resource set pointed to by the navigation property
181
     * @param ResourceProperty   $targetProperty       The navigation property to retrieve
182
     * @param FilterInfo|null    $filter               The $filter parameter of the OData query.  NULL if none specified
183
     * @param mixed|null         $orderBy              sorted order if we want to get the data in some specific order
184
     * @param int|null           $top                  number of records which need to be retrieved
185
     * @param int|null           $skip                 number of records which need to be skipped
186
     * @param SkipTokenInfo|null $skipToken            value indicating what records to skip
187
     *
188
     * @return QueryResult
189
     * @throws \Exception
190
     */
191
    public function getRelatedResourceSet(
192
        QueryType $queryType,
193
        ResourceSet $sourceResourceSet,
194
        $sourceEntityInstance,
195
        ResourceSet $targetResourceSet,
196
        ResourceProperty $targetProperty,
197
        FilterInfo $filter = null,
198
        $orderBy = null,
199 3
        $top = null,
200
        $skip = null,
201
        $skipToken = null
202
    ) {
203
        $source = $this->unpackSourceEntity($sourceEntityInstance);
204
        return $this->getReader()->getRelatedResourceSet(
205
            $queryType,
206
            $sourceResourceSet,
207
            $source,
208
            $targetResourceSet,
209
            $targetProperty,
210 3
            $filter,
211 3
            $orderBy,
212
            $top,
213 3
            $skip,
214 3
            $skipToken
215 3
        );
216 3
    }
217 3
218 3
    /**
219 3
     * Gets a related entity instance from an entity set identified by a key
220
     * IE: http://host/EntitySet(1L)/NavigationPropertyToCollection(33).
221 3
     *
222
     * @param ResourceSet      $sourceResourceSet    The entity set containing the source entity
223
     * @param object           $sourceEntityInstance the source entity instance
224
     * @param ResourceSet      $targetResourceSet    The entity set containing the entity to fetch
225
     * @param ResourceProperty $targetProperty       the metadata of the target property
226
     * @param KeyDescriptor    $keyDescriptor        The key identifying the entity to fetch
227
     *
228
     * @return Model|null Returns entity instance if found else null
229
     * @throws \Exception
230
     */
231
    public function getResourceFromRelatedResourceSet(
232
        ResourceSet $sourceResourceSet,
233
        $sourceEntityInstance,
234
        ResourceSet $targetResourceSet,
235
        ResourceProperty $targetProperty,
236
        KeyDescriptor $keyDescriptor
237
    ) {
238
        $source = $this->unpackSourceEntity($sourceEntityInstance);
239
        return $this->getReader()->getResourceFromRelatedResourceSet(
240
            $sourceResourceSet,
241
            $source,
242
            $targetResourceSet,
243
            $targetProperty,
244
            $keyDescriptor
245
        );
246
    }
247
248
    /**
249
     * Get related resource for a resource
250
     * IE: http://host/EntitySet(1L)/NavigationPropertyToSingleEntity
251
     * http://host/EntitySet?$expand=NavigationPropertyToSingleEntity.
252
     *
253
     * @param ResourceSet      $sourceResourceSet    The entity set containing the source entity
254
     * @param object           $sourceEntityInstance the source entity instance
255
     * @param ResourceSet      $targetResourceSet    The entity set containing the entity pointed to by the nav property
256
     * @param ResourceProperty $targetProperty       The navigation property to fetch
257
     *
258
     * @return Model|null The related resource if found else null
259
     * @throws \Exception
260
     */
261
    public function getRelatedResourceReference(
262
        ResourceSet $sourceResourceSet,
263
        $sourceEntityInstance,
264
        ResourceSet $targetResourceSet,
265
        ResourceProperty $targetProperty
266
    ) {
267
        $source = $this->unpackSourceEntity($sourceEntityInstance);
268
269
        $result = $this->getReader()->getRelatedResourceReference(
270
            $sourceResourceSet,
271
            $source,
272
            $targetResourceSet,
273
            $targetProperty
274
        );
275
        return $result;
276
    }
277
278
    /**
279
     * Updates a resource.
280
     *
281
     * @param ResourceSet       $sourceResourceSet    The entity set containing the source entity
282
     * @param Model|Relation    $sourceEntityInstance The source entity instance
283
     * @param KeyDescriptor     $keyDescriptor        The key identifying the entity to fetch
284
     * @param object            $data                 the New data for the entity instance
285
     * @param bool              $shouldUpdate         Should undefined values be updated or reset to default
286
     *
287
     * @return Model|null the new resource value if it is assignable or throw exception for null
288
     * @throws \Exception
289
     */
290
    public function updateResource(
291
        ResourceSet $sourceResourceSet,
292 1
        $sourceEntityInstance,
293
        KeyDescriptor $keyDescriptor,
294
        $data,
295
        $shouldUpdate = false
296
    ) {
297
        $verb = 'update';
298
        return $this->createUpdateMainWrapper($sourceResourceSet, $sourceEntityInstance, $data, $verb);
299 1
    }
300 1
    /**
301
     * Delete resource from a resource set.
302 1
     *
303
     * @param ResourceSet $sourceResourceSet
304 1
     * @param object      $sourceEntityInstance
305
     *
306 1
     * @return bool true if resources sucessfully deteled, otherwise false
307
     * @throws \Exception
308
     */
309 1
    public function deleteResource(
310
        ResourceSet $sourceResourceSet,
311
        $sourceEntityInstance
312
    ) {
313
        $source = $this->unpackSourceEntity($sourceEntityInstance);
314
315
        $verb = 'delete';
316
        if (!($source instanceof Model)) {
317
            throw new InvalidArgumentException('Source entity must be an Eloquent model.');
318 1
        }
319
320
        $class = $sourceResourceSet->getResourceType()->getInstanceType()->getName();
321
        $id = $source->getKey();
322 1
        $name = $source->getKeyName();
323 1
        $data = [$name => $id];
324 1
325 1
        $data = $this->getWriter()->createUpdateDeleteCore($source, $data, $class, $verb);
326 1
327
        $success = isset($data['id']);
328 1
        if ($success) {
329
            return true;
330 1
        }
331 1
        throw new ODataException('Target model not successfully deleted', 422);
332
    }
333
    /**
334 1
     * @param ResourceSet     $resourceSet          The entity set containing the entity to fetch
335
     * @param Model|Relation  $sourceEntityInstance The source entity instance
336
     * @param object          $data                 the New data for the entity instance
337
     *
338
     * @return Model|null                           returns the newly created model if successful,
339
     *                                              or null if model creation failed.
340
     * @throws \Exception
341
     */
342
    public function createResourceforResourceSet(
343 1
        ResourceSet $resourceSet,
344
        $sourceEntityInstance,
345
        $data
346
    ) {
347
        $verb = 'create';
348 1
        return $this->createUpdateMainWrapper($resourceSet, $sourceEntityInstance, $data, $verb);
349 1
    }
350
351 1
    /**
352
     * Puts an entity instance to entity set identified by a key.
353 1
     *
354
     * @param ResourceSet   $resourceSet   The entity set containing the entity to update
355 1
     * @param KeyDescriptor $keyDescriptor The key identifying the entity to update
356
     * @param $data
357
     *
358 1
     * @return bool|null Returns result of executing query
359
     */
360
    public function putResource(
361
        ResourceSet $resourceSet,
362
        KeyDescriptor $keyDescriptor,
363
        $data
364
    ) {
365
        // TODO: Implement putResource() method.
366
        return true;
367
    }
368
369
    /**
370 3
     * Create multiple new resources in a resource set.
371
     *
372 3
     * @param ResourceSet $sourceResourceSet The entity set containing the entity to fetch
373 3
     * @param object[]    $data              The new data for the entity instance
374
     *
375 3
     * @return object[] returns the newly created model if successful, or throws an exception if model creation failed
376
     * @throws InvalidOperationException
377
     * @throws \ReflectionException
378 3
     * @throw  \Exception
379 3
     */
380
    public function createBulkResourceforResourceSet(
381
        ResourceSet $sourceResourceSet,
382
        array $data
383
    ) {
384
        return $this->getBulk()->createBulkResourceForResourceSet($sourceResourceSet, $data);
385 3
    }
386
387
    /**
388 3
     * Updates a group of resources in a resource set.
389 2
     *
390 2
     * @param ResourceSet     $sourceResourceSet    The entity set containing the source entity
391 3
     * @param Model|Relation  $sourceEntityInstance The source entity instance
392
     * @param KeyDescriptor[] $keyDescriptor        The key identifying the entity to fetch
393
     * @param object[]        $data                 The new data for the entity instances
394
     * @param bool            $shouldUpdate         Should undefined values be updated or reset to default
395
     *
396
     * @return object[] the new resource value if it is assignable, or throw exception for null
397 3
     * @throw  \Exception
398 3
     * @throws InvalidOperationException
399 3
     */
400 3
    public function updateBulkResource(
401 3
        ResourceSet $sourceResourceSet,
402
        $sourceEntityInstance,
403 3
        array $keyDescriptor,
404 3
        array $data,
405 3
        $shouldUpdate = false
406 3
    ) {
407 2
        return $this->getBulk()
408 2
            ->updateBulkResource(
409 2
                $sourceResourceSet,
410 2
                $sourceEntityInstance,
411 2
                $keyDescriptor,
412
                $data,
413
                $shouldUpdate
414
            );
415 2
    }
416
417
    /**
418 3
     * Attaches child model to parent model.
419 3
     *
420
     * @param ResourceSet $sourceResourceSet
421 3
     * @param Model       $sourceEntityInstance
422
     * @param ResourceSet $targetResourceSet
423 3
     * @param Model       $targetEntityInstance
424
     * @param $navPropName
425
     *
426 3
     * @return bool
427 3
     * @throws InvalidOperationException
428 3
     */
429 3
    public function hookSingleModel(
430
        ResourceSet $sourceResourceSet,
431 3
        $sourceEntityInstance,
432
        ResourceSet $targetResourceSet,
433
        $targetEntityInstance,
434 3
        $navPropName
435
    ) {
436
        return $this->getModelHook()->hookSingleModel(
437
            $sourceResourceSet,
438
            $sourceEntityInstance,
439 3
            $targetResourceSet,
440
            $targetEntityInstance,
441
            $navPropName
442
        );
443
    }
444
445
    /**
446
     * Removes child model from parent model.
447
     *
448
     * @param ResourceSet $sourceResourceSet
449
     * @param Model       $sourceEntityInstance
450
     * @param ResourceSet $targetResourceSet
451
     * @param Model       $targetEntityInstance
452
     * @param $navPropName
453
     *
454
     * @return bool
455
     * @throws InvalidOperationException
456
     */
457
    public function unhookSingleModel(
458
        ResourceSet $sourceResourceSet,
459
        $sourceEntityInstance,
460
        ResourceSet $targetResourceSet,
461
        $targetEntityInstance,
462
        $navPropName
463
    ) {
464
        return $this->getModelHook()->unhookSingleModel(
465
            $sourceResourceSet,
466
            $sourceEntityInstance,
467
            $targetResourceSet,
468
            $targetEntityInstance,
469
            $navPropName
470
        );
471
    }
472
473
    /**
474
     * Start database transaction.
475
     * @param bool $isBulk
476
     */
477
    public function startTransaction($isBulk = false)
478
    {
479
        self::$touchList = [];
480
        self::$inBatch = true === $isBulk;
481
        DB::beginTransaction();
482
    }
483
484
    /**
485
     * Commit database transaction.
486
     */
487
    public function commitTransaction()
488
    {
489
        // fire model save again, to give Laravel app final chance to finalise anything that needs finalising after
490
        // batch processing
491
        foreach (self::$touchList as $model) {
492
            $model->save();
493
        }
494
495
        DB::commit();
496
        self::$touchList = [];
497
        self::$inBatch = false;
498
    }
499
500
    /**
501
     * Abort database transaction.
502
     */
503
    public function rollBackTransaction()
504
    {
505
        DB::rollBack();
506
        self::$touchList = [];
507
        self::$inBatch = false;
508
    }
509
510
    public static function queueModel(Model &$model)
511
    {
512
        // if we're not processing a batch, don't queue anything
513
        if (!self::$inBatch) {
514
            return;
515
        }
516
        // if we are in a batch, add to queue to process on transaction commit
517
        self::$touchList[] = $model;
518
    }
519
520
    /**
521
     * @param ResourceSet $resourceSet
522
     * @param Model|Relation|null $sourceEntityInstance
523
     * @param mixed $data
524
     * @param mixed $verb
525
     * @return Model|null
526
     * @throws InvalidOperationException
527
     * @throws ODataException
528
     */
529
    protected function createUpdateMainWrapper(ResourceSet $resourceSet, $sourceEntityInstance, $data, $verb)
530
    {
531
        /** @var Model|null $source */
532
        $source = $this->unpackSourceEntity($sourceEntityInstance);
533
534
        $result = $this->getWriter()->createUpdateCoreWrapper($resourceSet, $data, $verb, $source);
535
        if (null !== $result) {
536
            self::queueModel($result);
537
        }
538
        return $result;
539
    }
540
541
    /**
542
     * @return LaravelWriteQuery
543
     */
544
    public function getWriter()
545
    {
546
        return $this->writer;
547
    }
548
}
549