Passed
Pull Request — master (#160)
by Alex
04:44
created

LaravelQuery::getBulk()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 1
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 3
rs 10
c 0
b 0
f 0
ccs 1
cts 1
cp 1
cc 1
eloc 1
nc 1
nop 0
crap 1
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\BelongsTo;
12
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
13
use Illuminate\Database\Eloquent\Relations\HasOneOrMany;
14
use Illuminate\Database\Eloquent\Relations\Relation;
15
use Illuminate\Support\Facades\App;
16
use Illuminate\Support\Facades\DB;
17
use POData\Common\InvalidOperationException;
18
use POData\Common\ODataException;
19
use POData\Providers\Metadata\ResourceProperty;
20
use POData\Providers\Metadata\ResourceSet;
21
use POData\Providers\Query\IQueryProvider;
22
use POData\Providers\Query\QueryResult;
23
use POData\Providers\Query\QueryType;
24
use POData\UriProcessor\QueryProcessor\ExpressionParser\FilterInfo;
25
use POData\UriProcessor\QueryProcessor\OrderByParser\InternalOrderByInfo;
26 5
use POData\UriProcessor\QueryProcessor\SkipTokenParser\SkipTokenInfo;
27
use POData\UriProcessor\ResourcePathProcessor\SegmentParser\KeyDescriptor;
28
use Symfony\Component\Process\Exception\InvalidArgumentException;
29 5
30 5
class LaravelQuery implements IQueryProvider
31 5
{
32 5
    protected $expression;
33
    protected $auth;
34
    protected $reader;
35
    protected $modelHook;
36
    protected $bulk;
37
    public $queryProviderClassName;
38
    private $verbMap = [];
39
    protected $metadataProvider;
40
    protected $controllerContainer;
41
    private static $touchList = [];
42
    private static $inBatch;
43
44
    public function __construct(AuthInterface $auth = null)
45
    {
46
        /* MySQLExpressionProvider();*/
47
        $this->expression = new LaravelExpressionProvider(); //PHPExpressionProvider('expression');
48
        $this->queryProviderClassName = get_class($this);
49
        $this->auth = isset($auth) ? $auth : new NullAuthProvider();
50
        $this->reader = new LaravelReadQuery($this->auth);
51
        $this->modelHook = new LaravelHookQuery($this->auth);
52
        $this->bulk = new LaravelBulkQuery($this, $this->auth);
53
        $this->metadataProvider = new MetadataProvider(App::make('app'));
54
        $this->controllerContainer = App::make('metadataControllers');
55
        self::$touchList = [];
56
        self::$inBatch = false;
57
    }
58
59
    /**
60
     * Indicates if the QueryProvider can handle ordered paging, this means respecting order, skip, and top parameters
61
     * If the query provider can not handle ordered paging, it must return the entire result set and POData will
62
     * perform the ordering and paging.
63
     *
64
     * @return Boolean True if the query provider can handle ordered paging, false if POData should perform the paging
65
     */
66
    public function handlesOrderedPaging()
67
    {
68
        return true;
69
    }
70 3
71
    /**
72
     * Gets the expression provider used by to compile OData expressions into expression used by this query provider.
73
     *
74
     * @return \POData\Providers\Expression\IExpressionProvider
75
     */
76
    public function getExpressionProvider()
77
    {
78
        return $this->expression;
79 3
    }
80
81
    /**
82 3
     * Gets the LaravelReadQuery instance used to handle read queries (repetitious, nyet?).
83 1
     *
84 1
     * @return LaravelReadQuery
85
     */
86 3
    public function getReader()
87 3
    {
88 3
        return $this->reader;
89
    }
90 3
91
    /**
92
     * Gets the LaravelHookQuery instance used to handle hook/unhook queries (repetitious, nyet?).
93
     *
94
     * @return LaravelHookQuery
95
     */
96
    public function getModelHook()
97 1
    {
98
        return $this->modelHook;
99
    }
100 3
101 1
    /**
102 1
     * Gets the LaravelBulkQuery instance used to handle bulk queries (repetitious, nyet?).
103 3
     *
104 1
     * @return LaravelBulkQuery
105 1
     */
106
    public function getBulk()
107 3
    {
108
        return $this->bulk;
109 3
    }
110
111
    /**
112
     * Dig out local copy of POData-Laravel metadata provider.
113
     *
114
     * @return MetadataProvider
115
     */
116
    public function getMetadataProvider()
117 3
    {
118 1
        return $this->metadataProvider;
119 1
    }
120 1
121 1
    /**
122 1
     * Dig out local copy of controller metadata mapping.
123 3
     *
124 3
     * @return MetadataControllerContainer
125
     */
126
    public function getControllerContainer()
127 3
    {
128 3
        assert(null !== $this->controllerContainer, get_class($this->controllerContainer));
129 3
        return $this->controllerContainer;
130
    }
131
132
    public function getVerbMap()
133
    {
134
        if (0 == count($this->verbMap)) {
135
            $this->verbMap['create'] = ActionVerb::CREATE();
0 ignored issues
show
Bug introduced by
The method CREATE() does not exist on AlgoWeb\PODataLaravel\Enums\ActionVerb. Since you implemented __callStatic, consider adding a @method annotation. ( Ignorable by Annotation )

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

135
            /** @scrutinizer ignore-call */ 
136
            $this->verbMap['create'] = ActionVerb::CREATE();
Loading history...
136
            $this->verbMap['update'] = ActionVerb::UPDATE();
0 ignored issues
show
Bug introduced by
The method UPDATE() does not exist on AlgoWeb\PODataLaravel\Enums\ActionVerb. Since you implemented __callStatic, consider adding a @method annotation. ( Ignorable by Annotation )

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

136
            /** @scrutinizer ignore-call */ 
137
            $this->verbMap['update'] = ActionVerb::UPDATE();
Loading history...
137
            $this->verbMap['delete'] = ActionVerb::DELETE();
0 ignored issues
show
Bug introduced by
The method DELETE() does not exist on AlgoWeb\PODataLaravel\Enums\ActionVerb. Since you implemented __callStatic, consider adding a @method annotation. ( Ignorable by Annotation )

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

137
            /** @scrutinizer ignore-call */ 
138
            $this->verbMap['delete'] = ActionVerb::DELETE();
Loading history...
138
        }
139
        return $this->verbMap;
140
    }
141
142
    /**
143
     * Gets collection of entities belongs to an entity set
144
     * IE: http://host/EntitySet
145
     *  http://host/EntitySet?$skip=10&$top=5&filter=Prop gt Value.
146
     *
147
     * @param QueryType                $queryType            Is this is a query for a count, entities,
148
     *                                                       or entities-with-count?
149
     * @param ResourceSet              $resourceSet          The entity set containing the entities to fetch
150
     * @param FilterInfo|null          $filterInfo           The $filter parameter of the OData query.  NULL if absent
151
     * @param null|InternalOrderByInfo $orderBy              sorted order if we want to get the data in some
152
     *                                                       specific order
153
     * @param int|null                 $top                  number of records which need to be retrieved
154
     * @param int|null                 $skip                 number of records which need to be skipped
155
     * @param SkipTokenInfo|null       $skipToken            value indicating what records to skip
156
     * @param string[]|null            $eagerLoad            array of relations to eager load
157
     * @param Model|Relation|null      $sourceEntityInstance Starting point of query
158
     *
159
     * @return QueryResult
160
     */
161
    public function getResourceSet(
162
        QueryType $queryType,
163
        ResourceSet $resourceSet,
164
        $filterInfo = null,
165
        $orderBy = null,
166
        $top = null,
167
        $skip = null,
168
        $skipToken = null,
169
        array $eagerLoad = null,
170
        $sourceEntityInstance = null
171
    ) {
172
        $source = $this->unpackSourceEntity($sourceEntityInstance);
173
        return $this->getReader()->getResourceSet(
174
            $queryType,
175
            $resourceSet,
176
            $filterInfo,
177
            $orderBy,
178
            $top,
179
            $skip,
180
            $skipToken,
181
            $eagerLoad,
182
            $source
183
        );
184
    }
185
    /**
186
     * Gets an entity instance from an entity set identified by a key
187
     * IE: http://host/EntitySet(1L)
188
     * http://host/EntitySet(KeyA=2L,KeyB='someValue').
189
     *
190
     * @param ResourceSet        $resourceSet   The entity set containing the entity to fetch
191
     * @param KeyDescriptor|null $keyDescriptor The key identifying the entity to fetch
192
     * @param string[]|null      $eagerLoad     array of relations to eager load
193
     *
194
     * @return Model|null Returns entity instance if found else null
195
     */
196
    public function getResourceFromResourceSet(
197
        ResourceSet $resourceSet,
198
        KeyDescriptor $keyDescriptor = null,
199 3
        array $eagerLoad = null
200
    ) {
201
        return $this->getReader()->getResourceFromResourceSet($resourceSet, $keyDescriptor, $eagerLoad);
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->getReader(...Descriptor, $eagerLoad) also could return the type Illuminate\Database\Eloquent\Model which is incompatible with the return type mandated by POData\Providers\Query\I...sourceFromResourceSet() of object|null.
Loading history...
202
    }
203
204
    /**
205
     * Get related resource set for a resource
206
     * IE: http://host/EntitySet(1L)/NavigationPropertyToCollection
207
     * http://host/EntitySet?$expand=NavigationPropertyToCollection.
208
     *
209
     * @param QueryType          $queryType            Is this is a query for a count, entities, or entities-with-count
210 3
     * @param ResourceSet        $sourceResourceSet    The entity set containing the source entity
211 3
     * @param object             $sourceEntityInstance The source entity instance
212
     * @param ResourceSet        $targetResourceSet    The resource set pointed to by the navigation property
213 3
     * @param ResourceProperty   $targetProperty       The navigation property to retrieve
214 3
     * @param FilterInfo|null    $filter               The $filter parameter of the OData query.  NULL if none specified
215 3
     * @param mixed|null         $orderBy              sorted order if we want to get the data in some specific order
216 3
     * @param int|null           $top                  number of records which need to be retrieved
217 3
     * @param int|null           $skip                 number of records which need to be skipped
218 3
     * @param SkipTokenInfo|null $skipToken            value indicating what records to skip
219 3
     *
220
     * @return QueryResult
221 3
     */
222
    public function getRelatedResourceSet(
223
        QueryType $queryType,
224
        ResourceSet $sourceResourceSet,
225
        $sourceEntityInstance,
226
        ResourceSet $targetResourceSet,
227
        ResourceProperty $targetProperty,
228
        FilterInfo $filter = null,
229
        $orderBy = null,
230
        $top = null,
231
        $skip = null,
232
        $skipToken = null
233
    ) {
234
        $source = $this->unpackSourceEntity($sourceEntityInstance);
235
        return $this->getReader()->getRelatedResourceSet(
236
            $queryType,
237
            $sourceResourceSet,
238
            $source,
239
            $targetResourceSet,
240
            $targetProperty,
241
            $filter,
242
            $orderBy,
243
            $top,
244
            $skip,
245
            $skipToken
246
        );
247
    }
248
249
    /**
250
     * Gets a related entity instance from an entity set identified by a key
251
     * IE: http://host/EntitySet(1L)/NavigationPropertyToCollection(33).
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 to fetch
256
     * @param ResourceProperty $targetProperty       the metadata of the target property
257
     * @param KeyDescriptor    $keyDescriptor        The key identifying the entity to fetch
258
     *
259
     * @return Model|null Returns entity instance if found else null
260
     */
261
    public function getResourceFromRelatedResourceSet(
262
        ResourceSet $sourceResourceSet,
263
        $sourceEntityInstance,
264
        ResourceSet $targetResourceSet,
265
        ResourceProperty $targetProperty,
266
        KeyDescriptor $keyDescriptor
267
    ) {
268
        $source = $this->unpackSourceEntity($sourceEntityInstance);
269
        return $this->getReader()->getResourceFromRelatedResourceSet(
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->getReader(...operty, $keyDescriptor) also could return the type Illuminate\Database\Eloquent\Model which is incompatible with the return type mandated by POData\Providers\Query\I...romRelatedResourceSet() of object|null.
Loading history...
270
            $sourceResourceSet,
271
            $source,
272
            $targetResourceSet,
273
            $targetProperty,
274
            $keyDescriptor
275
        );
276
    }
277
278
    /**
279
     * Get related resource for a resource
280
     * IE: http://host/EntitySet(1L)/NavigationPropertyToSingleEntity
281
     * http://host/EntitySet?$expand=NavigationPropertyToSingleEntity.
282
     *
283
     * @param ResourceSet      $sourceResourceSet    The entity set containing the source entity
284
     * @param object           $sourceEntityInstance the source entity instance
285
     * @param ResourceSet      $targetResourceSet    The entity set containing the entity pointed to by the nav property
286
     * @param ResourceProperty $targetProperty       The navigation property to fetch
287
     *
288
     * @return object|null The related resource if found else null
289
     */
290
    public function getRelatedResourceReference(
291
        ResourceSet $sourceResourceSet,
292 1
        $sourceEntityInstance,
293
        ResourceSet $targetResourceSet,
294
        ResourceProperty $targetProperty
295
    ) {
296
        $source = $this->unpackSourceEntity($sourceEntityInstance);
297
298
        $result = $this->getReader()->getRelatedResourceReference(
299 1
            $sourceResourceSet,
300 1
            $source,
301
            $targetResourceSet,
302 1
            $targetProperty
303
        );
304 1
        return $result;
305
    }
306 1
307
    /**
308
     * Updates a resource.
309 1
     *
310
     * @param ResourceSet   $sourceResourceSet    The entity set containing the source entity
311
     * @param object        $sourceEntityInstance The source entity instance
312
     * @param KeyDescriptor $keyDescriptor        The key identifying the entity to fetch
313
     * @param object        $data                 the New data for the entity instance
314
     * @param bool          $shouldUpdate         Should undefined values be updated or reset to default
315
     *
316
     * @return object|null the new resource value if it is assignable or throw exception for null
317
     */
318 1 View Code Duplication
    public function updateResource(
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
319
        ResourceSet $sourceResourceSet,
320
        $sourceEntityInstance,
321
        KeyDescriptor $keyDescriptor,
322 1
        $data,
323 1
        $shouldUpdate = false
324 1
    ) {
325 1
        $source = $this->unpackSourceEntity($sourceEntityInstance);
326 1
327
        $verb = 'update';
328 1
        $result = $this->createUpdateCoreWrapper($sourceResourceSet, $data, $verb, $source);
329
        LaravelQuery::queueModel($result);
0 ignored issues
show
Coding Style introduced by
As per coding style, self should be used for accessing local static members.

This check looks for accesses to local static members using the fully qualified name instead of self::.

<?php

class Certificate {
    const TRIPLEDES_CBC = 'ASDFGHJKL';

    private $key;

    public function __construct()
    {
        $this->key = Certificate::TRIPLEDES_CBC;
    }
}

While this is perfectly valid, the fully qualified name of Certificate::TRIPLEDES_CBC could just as well be replaced by self::TRIPLEDES_CBC. Referencing local members with self:: assured the access will still work when the class is renamed, makes it perfectly clear that the member is in fact local and will usually be shorter.

Loading history...
330 1
        return $result;
331 1
    }
332
    /**
333
     * Delete resource from a resource set.
334 1
     *
335
     * @param ResourceSet $sourceResourceSet
336
     * @param object      $sourceEntityInstance
337
     *
338
     * return bool true if resources sucessfully deteled, otherwise false
339
     */
340
    public function deleteResource(
341
        ResourceSet $sourceResourceSet,
342
        $sourceEntityInstance
343 1
    ) {
344
        $source = $this->unpackSourceEntity($sourceEntityInstance);
345
346
        $verb = 'delete';
347
        if (!($source instanceof Model)) {
348 1
            throw new InvalidArgumentException('Source entity must be an Eloquent model.');
349 1
        }
350
351 1
        $class = $sourceResourceSet->getResourceType()->getInstanceType()->getName();
352
        $id = $source->getKey();
353 1
        $name = $source->getKeyName();
354
        $data = [$name => $id];
355 1
356
        $data = $this->createUpdateDeleteCore($source, $data, $class, $verb);
357
358 1
        $success = isset($data['id']);
359
        if ($success) {
360
            return true;
361
        }
362
        throw new ODataException('Target model not successfully deleted', 422);
363
    }
364
    /**
365
     * @param ResourceSet $resourceSet          The entity set containing the entity to fetch
366
     * @param object      $sourceEntityInstance The source entity instance
367
     * @param object      $data                 the New data for the entity instance
368
     *
369
     * @returns object|null                     returns the newly created model if successful,
370 3
     *                                          or null if model creation failed.
371
     */
372 3 View Code Duplication
    public function createResourceforResourceSet(
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
373 3
        ResourceSet $resourceSet,
374
        $sourceEntityInstance,
375 3
        $data
376
    ) {
377
        $source = $this->unpackSourceEntity($sourceEntityInstance);
378 3
379 3
        $verb = 'create';
380
        $result = $this->createUpdateCoreWrapper($resourceSet, $data, $verb, $source);
381
        LaravelQuery::queueModel($result);
0 ignored issues
show
Coding Style introduced by
As per coding style, self should be used for accessing local static members.

This check looks for accesses to local static members using the fully qualified name instead of self::.

<?php

class Certificate {
    const TRIPLEDES_CBC = 'ASDFGHJKL';

    private $key;

    public function __construct()
    {
        $this->key = Certificate::TRIPLEDES_CBC;
    }
}

While this is perfectly valid, the fully qualified name of Certificate::TRIPLEDES_CBC could just as well be replaced by self::TRIPLEDES_CBC. Referencing local members with self:: assured the access will still work when the class is renamed, makes it perfectly clear that the member is in fact local and will usually be shorter.

Loading history...
382
        return $result;
383
    }
384
385 3
    /**
386
     * @param $sourceEntityInstance
387
     * @param $data
388 3
     * @param $class
389 2
     * @param string $verb
390 2
     *
391 3
     * @throws ODataException
392
     * @throws InvalidOperationException
393
     * @return array|mixed
394
     */
395
    private function createUpdateDeleteCore($sourceEntityInstance, $data, $class, $verb)
396
    {
397 3
        $raw = App::make('metadataControllers');
398 3
        $map = $raw->getMetadata();
399 3
400 3
        if (!array_key_exists($class, $map)) {
401 3
            throw new \POData\Common\InvalidOperationException('Controller mapping missing for class ' . $class . '.');
402
        }
403 3
        $goal = $raw->getMapping($class, $verb);
404 3
        if (null == $goal) {
405 3
            throw new \POData\Common\InvalidOperationException(
406 3
                'Controller mapping missing for ' . $verb . ' verb on class ' . $class . '.'
407 2
            );
408 2
        }
409 2
410 2
        assert(null !== $data, 'Data must not be null');
411 2
        if (is_object($data)) {
412
            $arrayData = (array) $data;
413
        } else {
414
            $arrayData = $data;
415 2
        }
416
        if (!is_array($arrayData)) {
417
            throw \POData\Common\ODataException::createPreConditionFailedError(
418 3
                'Data not resolvable to key-value array.'
419 3
            );
420
        }
421 3
422
        $controlClass = $goal['controller'];
423 3
        $method = $goal['method'];
424
        $paramList = $goal['parameters'];
425
        $controller = App::make($controlClass);
426 3
        $parms = $this->createUpdateDeleteProcessInput($arrayData, $paramList, $sourceEntityInstance);
427 3
        unset($data);
428 3
429 3
        $result = call_user_func_array(array($controller, $method), $parms);
430
431 3
        return $this->createUpdateDeleteProcessOutput($result);
432
    }
433
434 3
    /**
435
     * Puts an entity instance to entity set identified by a key.
436
     *
437
     * @param ResourceSet   $resourceSet   The entity set containing the entity to update
438
     * @param KeyDescriptor $keyDescriptor The key identifying the entity to update
439 3
     * @param $data
440
     *
441
     * @return bool|null Returns result of executing query
442
     */
443
    public function putResource(
444
        ResourceSet $resourceSet,
445
        KeyDescriptor $keyDescriptor,
446
        $data
447
    ) {
448
        // TODO: Implement putResource() method.
0 ignored issues
show
Coding Style Best Practice introduced by
Comments for TODO tasks are often forgotten in the code; it might be better to use a dedicated issue tracker.
Loading history...
449
        return true;
450
    }
451
452
    /**
453
     * @param ResourceSet $sourceResourceSet
454
     * @param $data
455
     * @param                            $verb
456
     * @param  Model|null                $source
457
     * @throws InvalidOperationException
458
     * @throws ODataException
459
     * @return mixed
460
     */
461
    protected function createUpdateCoreWrapper(ResourceSet $sourceResourceSet, $data, $verb, Model $source = null)
462
    {
463
        $lastWord = 'update' == $verb ? 'updated' : 'created';
464
        $class = $sourceResourceSet->getResourceType()->getInstanceType()->getName();
465
        if (!$this->auth->canAuth($this->getVerbMap()[$verb], $class, $source)) {
466
            throw new ODataException('Access denied', 403);
467
        }
468
469
        $payload = $this->createUpdateDeleteCore($source, $data, $class, $verb);
470
471
        $success = isset($payload['id']);
472
473
        if ($success) {
474
            try {
475
                return $class::findOrFail($payload['id']);
476
            } catch (\Exception $e) {
477
                throw new ODataException($e->getMessage(), 500);
478
            }
479
        }
480
        throw new ODataException('Target model not successfully ' . $lastWord, 422);
481
    }
482
483
    /**
484
     * @param $data
485
     * @param $paramList
486
     * @param  Model|null $sourceEntityInstance
487
     * @return array
488
     */
489
    protected function createUpdateDeleteProcessInput($data, $paramList, Model $sourceEntityInstance = null)
490
    {
491
        $parms = [];
492
493
        foreach ($paramList as $spec) {
494
            $varType = isset($spec['type']) ? $spec['type'] : null;
495
            $varName = $spec['name'];
496
            if (null == $varType) {
497
                $parms[] = ('id' == $varName) ? $sourceEntityInstance->getKey() : $sourceEntityInstance->$varName;
0 ignored issues
show
Bug introduced by
The method getKey() does not exist on null. ( Ignorable by Annotation )

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

497
                $parms[] = ('id' == $varName) ? $sourceEntityInstance->/** @scrutinizer ignore-call */ getKey() : $sourceEntityInstance->$varName;

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
498
                continue;
499
            }
500
            // TODO: Give this smarts and actively pick up instantiation details
0 ignored issues
show
Coding Style Best Practice introduced by
Comments for TODO tasks are often forgotten in the code; it might be better to use a dedicated issue tracker.
Loading history...
501
            $var = new $varType();
502
            if ($spec['isRequest']) {
503
                $var->setMethod('POST');
504
                $var->request = new \Symfony\Component\HttpFoundation\ParameterBag($data);
505
            }
506
            $parms[] = $var;
507
        }
508
        return $parms;
509
    }
510
511
    /**
512
     * @param $result
513
     * @throws ODataException
514
     * @return array|mixed
515
     */
516 View Code Duplication
    private function createUpdateDeleteProcessOutput($result)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
517
    {
518
        if (!($result instanceof \Illuminate\Http\JsonResponse)) {
519
            throw ODataException::createInternalServerError('Controller response not well-formed json.');
520
        }
521
        $outData = $result->getData();
522
        if (is_object($outData)) {
523
            $outData = (array) $outData;
524
        }
525
526
        if (!is_array($outData)) {
527
            throw ODataException::createInternalServerError('Controller response does not have an array.');
528
        }
529
        if (!(key_exists('id', $outData) && key_exists('status', $outData) && key_exists('errors', $outData))) {
530
            throw ODataException::createInternalServerError(
531
                'Controller response array missing at least one of id, status and/or errors fields.'
532
            );
533
        }
534
        return $outData;
535
    }
536
537
    /**
538
     * @param $sourceEntityInstance
539
     * @return mixed|null|\object[]
540
     */
541
    private function unpackSourceEntity($sourceEntityInstance)
542
    {
543
        if ($sourceEntityInstance instanceof QueryResult) {
544
            $source = $sourceEntityInstance->results;
545
            $source = (is_array($source)) ? $source[0] : $source;
546
            return $source;
547
        }
548
        return $sourceEntityInstance;
549
    }
550
551
    /**
552
     * Create multiple new resources in a resource set.
553
     *
554
     * @param ResourceSet $sourceResourceSet The entity set containing the entity to fetch
555
     * @param object[]    $data              The new data for the entity instance
556
     *
557
     * @return object[] returns the newly created model if successful, or throws an exception if model creation failed
558
     * @throw  \Exception
559
     */
560
    public function createBulkResourceforResourceSet(
561
        ResourceSet $sourceResourceSet,
562
        array $data
563
    ) {
564
        return $this->getBulk()->createBulkResourceForResourceSet($sourceResourceSet, $data);
565
    }
566
567
    /**
568
     * Updates a group of resources in a resource set.
569
     *
570
     * @param ResourceSet     $sourceResourceSet    The entity set containing the source entity
571
     * @param object          $sourceEntityInstance The source entity instance
572
     * @param KeyDescriptor[] $keyDescriptor        The key identifying the entity to fetch
573
     * @param object[]        $data                 The new data for the entity instances
574
     * @param bool            $shouldUpdate         Should undefined values be updated or reset to default
575
     *
576
     * @return object[] the new resource value if it is assignable, or throw exception for null
577
     * @throw  \Exception
578
     */
579
    public function updateBulkResource(
580
        ResourceSet $sourceResourceSet,
581
        $sourceEntityInstance,
582
        array $keyDescriptor,
583
        array $data,
584
        $shouldUpdate = false
585
    ) {
586
        return $this->getBulk()
587
            ->updateBulkResource(
588
                $sourceResourceSet,
589
                $sourceEntityInstance,
590
                $keyDescriptor,
591
                $data,
592
                $shouldUpdate
593
            );
594
    }
595
596
    /**
597
     * Attaches child model to parent model.
598
     *
599
     * @param ResourceSet $sourceResourceSet
600
     * @param object      $sourceEntityInstance
601
     * @param ResourceSet $targetResourceSet
602
     * @param object      $targetEntityInstance
603
     * @param $navPropName
604
     *
605
     * @return bool
606
     */
607
    public function hookSingleModel(
608
        ResourceSet $sourceResourceSet,
609
        $sourceEntityInstance,
610
        ResourceSet $targetResourceSet,
611
        $targetEntityInstance,
612
        $navPropName
613
    ) {
614
        return $this->getModelHook()->hookSingleModel(
615
            $sourceResourceSet,
616
            $sourceEntityInstance,
617
            $targetResourceSet,
618
            $targetEntityInstance,
619
            $navPropName
620
        );
621
    }
622
623
    /**
624
     * Removes child model from parent model.
625
     *
626
     * @param ResourceSet $sourceResourceSet
627
     * @param object      $sourceEntityInstance
628
     * @param ResourceSet $targetResourceSet
629
     * @param object      $targetEntityInstance
630
     * @param $navPropName
631
     *
632
     * @return bool
633
     */
634
    public function unhookSingleModel(
635
        ResourceSet $sourceResourceSet,
636
        $sourceEntityInstance,
637
        ResourceSet $targetResourceSet,
638
        $targetEntityInstance,
639
        $navPropName
640
    ) {
641
        return $this->getModelHook()->unhookSingleModel(
642
            $sourceResourceSet,
643
            $sourceEntityInstance,
644
            $targetResourceSet,
645
            $targetEntityInstance,
646
            $navPropName
647
        );
648
    }
649
650
    /**
651
     * Start database transaction.
652
     */
653
    public function startTransaction($isBulk = false)
654
    {
655
        self::$touchList = [];
656
        self::$inBatch = true === $isBulk;
657
        DB::beginTransaction();
658
    }
659
660
    /**
661
     * Commit database transaction.
662
     */
663
    public function commitTransaction()
664
    {
665
        // fire model save again, to give Laravel app final chance to finalise anything that needs finalising after
666
        // batch processing
667
        foreach (self::$touchList as $model) {
668
            $model->save();
669
        }
670
671
        DB::commit();
672
        self::$touchList = [];
673
        self::$inBatch = false;
674
    }
675
676
    /**
677
     * Abort database transaction.
678
     */
679
    public function rollBackTransaction()
680
    {
681
        DB::rollBack();
682
        self::$touchList = [];
683
        self::$inBatch = false;
684
    }
685
686
    public static function queueModel(Model &$model)
687
    {
688
        // if we're not processing a batch, don't queue anything
689
        if (!self::$inBatch) {
690
            return;
691
        }
692
        // if we are in a batch, add to queue to process on transaction commit
693
        self::$touchList[] = $model;
694
    }
695
}
696