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

LaravelQuery::createUpdateDeleteProcessOutput()   B

Complexity

Conditions 7
Paths 7

Size

Total Lines 19
Code Lines 11

Duplication

Lines 19
Ratio 100 %

Code Coverage

Tests 0
CRAP Score 56

Importance

Changes 0
Metric Value
dl 19
loc 19
rs 8.2222
c 0
b 0
f 0
ccs 0
cts 0
cp 0
cc 7
eloc 11
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\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
    public function updateResource(
319
        ResourceSet $sourceResourceSet,
320
        $sourceEntityInstance,
321
        KeyDescriptor $keyDescriptor,
322 1
        $data,
323 1
        $shouldUpdate = false
324 1
    ) {
325 1
        $verb = 'update';
326 1
        return $this->createUpdateMainWrapper($sourceResourceSet, $sourceEntityInstance, $data, $verb);
327
    }
328 1
    /**
329
     * Delete resource from a resource set.
330 1
     *
331 1
     * @param ResourceSet $sourceResourceSet
332
     * @param object      $sourceEntityInstance
333
     *
334 1
     * return bool true if resources sucessfully deteled, otherwise false
335
     */
336
    public function deleteResource(
337
        ResourceSet $sourceResourceSet,
338
        $sourceEntityInstance
339
    ) {
340
        $source = $this->unpackSourceEntity($sourceEntityInstance);
341
342
        $verb = 'delete';
343 1
        if (!($source instanceof Model)) {
344
            throw new InvalidArgumentException('Source entity must be an Eloquent model.');
345
        }
346
347
        $class = $sourceResourceSet->getResourceType()->getInstanceType()->getName();
348 1
        $id = $source->getKey();
349 1
        $name = $source->getKeyName();
350
        $data = [$name => $id];
351 1
352
        $data = $this->createUpdateDeleteCore($source, $data, $class, $verb);
353 1
354
        $success = isset($data['id']);
355 1
        if ($success) {
356
            return true;
357
        }
358 1
        throw new ODataException('Target model not successfully deleted', 422);
359
    }
360
    /**
361
     * @param ResourceSet $resourceSet          The entity set containing the entity to fetch
362
     * @param object      $sourceEntityInstance The source entity instance
363
     * @param object      $data                 the New data for the entity instance
364
     *
365
     * @returns object|null                     returns the newly created model if successful,
366
     *                                          or null if model creation failed.
367
     */
368
    public function createResourceforResourceSet(
369
        ResourceSet $resourceSet,
370 3
        $sourceEntityInstance,
371
        $data
372 3
    ) {
373 3
        $verb = 'create';
374
        return $this->createUpdateMainWrapper($resourceSet, $sourceEntityInstance, $data, $verb);
375 3
    }
376
377
    /**
378 3
     * @param $sourceEntityInstance
379 3
     * @param $data
380
     * @param $class
381
     * @param string $verb
382
     *
383
     * @throws ODataException
384
     * @throws InvalidOperationException
385 3
     * @return array|mixed
386
     */
387
    private function createUpdateDeleteCore($sourceEntityInstance, $data, $class, $verb)
388 3
    {
389 2
        $raw = App::make('metadataControllers');
390 2
        $map = $raw->getMetadata();
391 3
392
        if (!array_key_exists($class, $map)) {
393
            throw new \POData\Common\InvalidOperationException('Controller mapping missing for class ' . $class . '.');
394
        }
395
        $goal = $raw->getMapping($class, $verb);
396
        if (null == $goal) {
397 3
            throw new \POData\Common\InvalidOperationException(
398 3
                'Controller mapping missing for ' . $verb . ' verb on class ' . $class . '.'
399 3
            );
400 3
        }
401 3
402
        assert(null !== $data, 'Data must not be null');
403 3
        if (is_object($data)) {
404 3
            $arrayData = (array) $data;
405 3
        } else {
406 3
            $arrayData = $data;
407 2
        }
408 2
        if (!is_array($arrayData)) {
409 2
            throw \POData\Common\ODataException::createPreConditionFailedError(
410 2
                'Data not resolvable to key-value array.'
411 2
            );
412
        }
413
414
        $controlClass = $goal['controller'];
415 2
        $method = $goal['method'];
416
        $paramList = $goal['parameters'];
417
        $controller = App::make($controlClass);
418 3
        $parms = $this->createUpdateDeleteProcessInput($arrayData, $paramList, $sourceEntityInstance);
419 3
        unset($data);
420
421 3
        $result = call_user_func_array(array($controller, $method), $parms);
422
423 3
        return $this->createUpdateDeleteProcessOutput($result);
424
    }
425
426 3
    /**
427 3
     * Puts an entity instance to entity set identified by a key.
428 3
     *
429 3
     * @param ResourceSet   $resourceSet   The entity set containing the entity to update
430
     * @param KeyDescriptor $keyDescriptor The key identifying the entity to update
431 3
     * @param $data
432
     *
433
     * @return bool|null Returns result of executing query
434 3
     */
435
    public function putResource(
436
        ResourceSet $resourceSet,
437
        KeyDescriptor $keyDescriptor,
438
        $data
439 3
    ) {
440
        // 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...
441
        return true;
442
    }
443
444
    /**
445
     * @param ResourceSet $sourceResourceSet
446
     * @param $data
447
     * @param                            $verb
448
     * @param  Model|null                $source
449
     * @throws InvalidOperationException
450
     * @throws ODataException
451
     * @return mixed
452
     */
453
    protected function createUpdateCoreWrapper(ResourceSet $sourceResourceSet, $data, $verb, Model $source = null)
454
    {
455
        $lastWord = 'update' == $verb ? 'updated' : 'created';
456
        $class = $sourceResourceSet->getResourceType()->getInstanceType()->getName();
457
        if (!$this->auth->canAuth($this->getVerbMap()[$verb], $class, $source)) {
458
            throw new ODataException('Access denied', 403);
459
        }
460
461
        $payload = $this->createUpdateDeleteCore($source, $data, $class, $verb);
462
463
        $success = isset($payload['id']);
464
465
        if ($success) {
466
            try {
467
                return $class::findOrFail($payload['id']);
468
            } catch (\Exception $e) {
469
                throw new ODataException($e->getMessage(), 500);
470
            }
471
        }
472
        throw new ODataException('Target model not successfully ' . $lastWord, 422);
473
    }
474
475
    /**
476
     * @param $data
477
     * @param $paramList
478
     * @param  Model|null $sourceEntityInstance
479
     * @return array
480
     */
481
    protected function createUpdateDeleteProcessInput($data, $paramList, Model $sourceEntityInstance = null)
482
    {
483
        $parms = [];
484
485
        foreach ($paramList as $spec) {
486
            $varType = isset($spec['type']) ? $spec['type'] : null;
487
            $varName = $spec['name'];
488
            if (null == $varType) {
489
                $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

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