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

LaravelQuery::createUpdateDeleteProcessOutput()   B

Complexity

Conditions 7
Paths 7

Size

Total Lines 19
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 56

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 11
c 1
b 0
f 0
dl 0
loc 19
ccs 0
cts 0
cp 0
rs 8.8333
cc 7
nc 7
nop 1
crap 56
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
        return $this->getWriter()->updateResource(
298
            $sourceResourceSet,
299 1
            $sourceEntityInstance,
300 1
            $keyDescriptor,
301
            $data,
302 1
            $shouldUpdate
303
        );
304 1
    }
305
306 1
    /**
307
     * Delete resource from a resource set.
308
     *
309 1
     * @param ResourceSet $sourceResourceSet
310
     * @param object      $sourceEntityInstance
311
     *
312
     * @return bool true if resources sucessfully deteled, otherwise false
313
     * @throws \Exception
314
     */
315
    public function deleteResource(
316
        ResourceSet $sourceResourceSet,
317
        $sourceEntityInstance
318 1
    ) {
319
        return $this->getWriter()->deleteResource($sourceResourceSet, $sourceEntityInstance);
320
    }
321
322 1
    /**
323 1
     * @param ResourceSet     $resourceSet          The entity set containing the entity to fetch
324 1
     * @param Model|Relation  $sourceEntityInstance The source entity instance
325 1
     * @param object          $data                 the New data for the entity instance
326 1
     *
327
     * @return Model|null                           returns the newly created model if successful,
328 1
     *                                              or null if model creation failed.
329
     * @throws \Exception
330 1
     */
331 1
    public function createResourceforResourceSet(
332
        ResourceSet $resourceSet,
333
        $sourceEntityInstance,
334 1
        $data
335
    ) {
336
        return $this->getWriter()->createResourceforResourceSet($resourceSet, $sourceEntityInstance, $data);
337
    }
338
339
    /**
340
     * Puts an entity instance to entity set identified by a key.
341
     *
342
     * @param ResourceSet   $resourceSet   The entity set containing the entity to update
343 1
     * @param KeyDescriptor $keyDescriptor The key identifying the entity to update
344
     * @param $data
345
     *
346
     * @return bool|null Returns result of executing query
347
     */
348 1
    public function putResource(
349 1
        ResourceSet $resourceSet,
350
        KeyDescriptor $keyDescriptor,
351 1
        $data
352
    ) {
353 1
        return $this->getWriter()->putResource($resourceSet, $keyDescriptor, $data);
354
    }
355 1
356
    /**
357
     * Create multiple new resources in a resource set.
358 1
     *
359
     * @param ResourceSet $sourceResourceSet The entity set containing the entity to fetch
360
     * @param object[]    $data              The new data for the entity instance
361
     *
362
     * @return object[] returns the newly created model if successful, or throws an exception if model creation failed
363
     * @throws InvalidOperationException
364
     * @throws \ReflectionException
365
     * @throw  \Exception
366
     */
367
    public function createBulkResourceforResourceSet(
368
        ResourceSet $sourceResourceSet,
369
        array $data
370 3
    ) {
371
        return $this->getBulk()->createBulkResourceForResourceSet($sourceResourceSet, $data);
372 3
    }
373 3
374
    /**
375 3
     * Updates a group of resources in a resource set.
376
     *
377
     * @param ResourceSet     $sourceResourceSet    The entity set containing the source entity
378 3
     * @param Model|Relation  $sourceEntityInstance The source entity instance
379 3
     * @param KeyDescriptor[] $keyDescriptor        The key identifying the entity to fetch
380
     * @param object[]        $data                 The new data for the entity instances
381
     * @param bool            $shouldUpdate         Should undefined values be updated or reset to default
382
     *
383
     * @return object[] the new resource value if it is assignable, or throw exception for null
384
     * @throw  \Exception
385 3
     * @throws InvalidOperationException
386
     */
387
    public function updateBulkResource(
388 3
        ResourceSet $sourceResourceSet,
389 2
        $sourceEntityInstance,
390 2
        array $keyDescriptor,
391 3
        array $data,
392
        $shouldUpdate = false
393
    ) {
394
        return $this->getBulk()
395
            ->updateBulkResource(
396
                $sourceResourceSet,
397 3
                $sourceEntityInstance,
398 3
                $keyDescriptor,
399 3
                $data,
400 3
                $shouldUpdate
401 3
            );
402
    }
403 3
404 3
    /**
405 3
     * Attaches child model to parent model.
406 3
     *
407 2
     * @param ResourceSet $sourceResourceSet
408 2
     * @param Model       $sourceEntityInstance
409 2
     * @param ResourceSet $targetResourceSet
410 2
     * @param Model       $targetEntityInstance
411 2
     * @param $navPropName
412
     *
413
     * @return bool
414
     * @throws InvalidOperationException
415 2
     */
416
    public function hookSingleModel(
417
        ResourceSet $sourceResourceSet,
418 3
        $sourceEntityInstance,
419 3
        ResourceSet $targetResourceSet,
420
        $targetEntityInstance,
421 3
        $navPropName
422
    ) {
423 3
        return $this->getModelHook()->hookSingleModel(
424
            $sourceResourceSet,
425
            $sourceEntityInstance,
426 3
            $targetResourceSet,
427 3
            $targetEntityInstance,
428 3
            $navPropName
429 3
        );
430
    }
431 3
432
    /**
433
     * Removes child model from parent model.
434 3
     *
435
     * @param ResourceSet $sourceResourceSet
436
     * @param Model       $sourceEntityInstance
437
     * @param ResourceSet $targetResourceSet
438
     * @param Model       $targetEntityInstance
439 3
     * @param $navPropName
440
     *
441
     * @return bool
442
     * @throws InvalidOperationException
443
     */
444
    public function unhookSingleModel(
445
        ResourceSet $sourceResourceSet,
446
        $sourceEntityInstance,
447
        ResourceSet $targetResourceSet,
448
        $targetEntityInstance,
449
        $navPropName
450
    ) {
451
        return $this->getModelHook()->unhookSingleModel(
452
            $sourceResourceSet,
453
            $sourceEntityInstance,
454
            $targetResourceSet,
455
            $targetEntityInstance,
456
            $navPropName
457
        );
458
    }
459
460
    /**
461
     * Start database transaction.
462
     * @param bool $isBulk
463
     */
464
    public function startTransaction($isBulk = false)
465
    {
466
        self::$touchList = [];
467
        self::$inBatch = true === $isBulk;
468
        DB::beginTransaction();
469
    }
470
471
    /**
472
     * Commit database transaction.
473
     */
474
    public function commitTransaction()
475
    {
476
        // fire model save again, to give Laravel app final chance to finalise anything that needs finalising after
477
        // batch processing
478
        foreach (self::$touchList as $model) {
479
            $model->save();
480
        }
481
482
        DB::commit();
483
        self::$touchList = [];
484
        self::$inBatch = false;
485
    }
486
487
    /**
488
     * Abort database transaction.
489
     */
490
    public function rollBackTransaction()
491
    {
492
        DB::rollBack();
493
        self::$touchList = [];
494
        self::$inBatch = false;
495
    }
496
497
    public static function queueModel(Model &$model)
498
    {
499
        // if we're not processing a batch, don't queue anything
500
        if (!self::$inBatch) {
501
            return;
502
        }
503
        // if we are in a batch, add to queue to process on transaction commit
504
        self::$touchList[] = $model;
505
    }
506
507
    /**
508
     * @return LaravelWriteQuery
509
     */
510
    public function getWriter()
511
    {
512
        return $this->writer;
513
    }
514
}
515