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

LaravelQuery::createBulkResourceforResourceSet()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

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