Passed
Pull Request — master (#171)
by Alex
04:02
created

LaravelQuery::updateResource()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 9
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 6
CRAP Score 1

Importance

Changes 0
Metric Value
eloc 2
dl 0
loc 9
ccs 6
cts 6
cp 1
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 5
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
        if (null === $this->controllerContainer) {
129 3
            throw new InvalidOperationException('Controller container must not be null');
130
        }
131
        return $this->controllerContainer;
132
    }
133
134
    public function getVerbMap()
135
    {
136
        if (0 == count($this->verbMap)) {
137
            $this->verbMap['create'] = ActionVerb::CREATE();
138
            $this->verbMap['update'] = ActionVerb::UPDATE();
139
            $this->verbMap['delete'] = ActionVerb::DELETE();
140
        }
141
        return $this->verbMap;
142
    }
143
144
    /**
145
     * Gets collection of entities belongs to an entity set
146
     * IE: http://host/EntitySet
147
     *  http://host/EntitySet?$skip=10&$top=5&filter=Prop gt Value.
148
     *
149
     * @param QueryType                $queryType            Is this is a query for a count, entities,
150
     *                                                       or entities-with-count?
151
     * @param ResourceSet              $resourceSet          The entity set containing the entities to fetch
152
     * @param FilterInfo|null          $filterInfo           The $filter parameter of the OData query.  NULL if absent
153
     * @param null|InternalOrderByInfo $orderBy              sorted order if we want to get the data in some
154
     *                                                       specific order
155
     * @param int|null                 $top                  number of records which need to be retrieved
156
     * @param int|null                 $skip                 number of records which need to be skipped
157
     * @param SkipTokenInfo|null       $skipToken            value indicating what records to skip
158
     * @param string[]|null            $eagerLoad            array of relations to eager load
159
     * @param Model|Relation|null      $sourceEntityInstance Starting point of query
160
     *
161
     * @return QueryResult
162
     */
163
    public function getResourceSet(
164
        QueryType $queryType,
165
        ResourceSet $resourceSet,
166
        $filterInfo = null,
167
        $orderBy = null,
168
        $top = null,
169
        $skip = null,
170
        $skipToken = null,
171
        array $eagerLoad = null,
172
        $sourceEntityInstance = null
173
    ) {
174
        $source = $this->unpackSourceEntity($sourceEntityInstance);
175
        return $this->getReader()->getResourceSet(
176
            $queryType,
177
            $resourceSet,
178
            $filterInfo,
179
            $orderBy,
180
            $top,
181
            $skip,
182
            $skipToken,
183
            $eagerLoad,
184
            $source
0 ignored issues
show
Bug introduced by
It seems like $source can also be of type object[]; however, parameter $sourceEntityInstance of AlgoWeb\PODataLaravel\Qu...Query::getResourceSet() does only seem to accept null|Illuminate\Database...uent\Relations\Relation, maybe add an additional type check? ( Ignorable by Annotation )

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

184
            /** @scrutinizer ignore-type */ $source
Loading history...
185
        );
186
    }
187
    /**
188
     * Gets an entity instance from an entity set identified by a key
189
     * IE: http://host/EntitySet(1L)
190
     * http://host/EntitySet(KeyA=2L,KeyB='someValue').
191
     *
192
     * @param ResourceSet        $resourceSet   The entity set containing the entity to fetch
193
     * @param KeyDescriptor|null $keyDescriptor The key identifying the entity to fetch
194
     * @param string[]|null      $eagerLoad     array of relations to eager load
195
     *
196
     * @return Model|null Returns entity instance if found else null
197
     */
198
    public function getResourceFromResourceSet(
199 3
        ResourceSet $resourceSet,
200
        KeyDescriptor $keyDescriptor = null,
201
        array $eagerLoad = null
202
    ) {
203
        return $this->getReader()->getResourceFromResourceSet($resourceSet, $keyDescriptor, $eagerLoad);
204
    }
205
206
    /**
207
     * Get related resource set for a resource
208
     * IE: http://host/EntitySet(1L)/NavigationPropertyToCollection
209
     * http://host/EntitySet?$expand=NavigationPropertyToCollection.
210 3
     *
211 3
     * @param QueryType          $queryType            Is this is a query for a count, entities, or entities-with-count
212
     * @param ResourceSet        $sourceResourceSet    The entity set containing the source entity
213 3
     * @param object             $sourceEntityInstance The source entity instance
214 3
     * @param ResourceSet        $targetResourceSet    The resource set pointed to by the navigation property
215 3
     * @param ResourceProperty   $targetProperty       The navigation property to retrieve
216 3
     * @param FilterInfo|null    $filter               The $filter parameter of the OData query.  NULL if none specified
217 3
     * @param mixed|null         $orderBy              sorted order if we want to get the data in some specific order
218 3
     * @param int|null           $top                  number of records which need to be retrieved
219 3
     * @param int|null           $skip                 number of records which need to be skipped
220
     * @param SkipTokenInfo|null $skipToken            value indicating what records to skip
221 3
     *
222
     * @return QueryResult
223
     */
224
    public function getRelatedResourceSet(
225
        QueryType $queryType,
226
        ResourceSet $sourceResourceSet,
227
        $sourceEntityInstance,
228
        ResourceSet $targetResourceSet,
229
        ResourceProperty $targetProperty,
230
        FilterInfo $filter = null,
231
        $orderBy = null,
232
        $top = null,
233
        $skip = null,
234
        $skipToken = null
235
    ) {
236
        $source = $this->unpackSourceEntity($sourceEntityInstance);
237
        return $this->getReader()->getRelatedResourceSet(
238
            $queryType,
239
            $sourceResourceSet,
240
            $source,
241
            $targetResourceSet,
242
            $targetProperty,
243
            $filter,
244
            $orderBy,
245
            $top,
246
            $skip,
247
            $skipToken
248
        );
249
    }
250
251
    /**
252
     * Gets a related entity instance from an entity set identified by a key
253
     * IE: http://host/EntitySet(1L)/NavigationPropertyToCollection(33).
254
     *
255
     * @param ResourceSet      $sourceResourceSet    The entity set containing the source entity
256
     * @param object           $sourceEntityInstance the source entity instance
257
     * @param ResourceSet      $targetResourceSet    The entity set containing the entity to fetch
258
     * @param ResourceProperty $targetProperty       the metadata of the target property
259
     * @param KeyDescriptor    $keyDescriptor        The key identifying the entity to fetch
260
     *
261
     * @return Model|null Returns entity instance if found else null
262
     */
263
    public function getResourceFromRelatedResourceSet(
264
        ResourceSet $sourceResourceSet,
265
        $sourceEntityInstance,
266
        ResourceSet $targetResourceSet,
267
        ResourceProperty $targetProperty,
268
        KeyDescriptor $keyDescriptor
269
    ) {
270
        $source = $this->unpackSourceEntity($sourceEntityInstance);
271
        return $this->getReader()->getResourceFromRelatedResourceSet(
272
            $sourceResourceSet,
273
            $source,
274
            $targetResourceSet,
275
            $targetProperty,
276
            $keyDescriptor
277
        );
278
    }
279
280
    /**
281
     * Get related resource for a resource
282
     * IE: http://host/EntitySet(1L)/NavigationPropertyToSingleEntity
283
     * http://host/EntitySet?$expand=NavigationPropertyToSingleEntity.
284
     *
285
     * @param ResourceSet      $sourceResourceSet    The entity set containing the source entity
286
     * @param object           $sourceEntityInstance the source entity instance
287
     * @param ResourceSet      $targetResourceSet    The entity set containing the entity pointed to by the nav property
288
     * @param ResourceProperty $targetProperty       The navigation property to fetch
289
     *
290
     * @return Model|null The related resource if found else null
291
     */
292 1
    public function getRelatedResourceReference(
293
        ResourceSet $sourceResourceSet,
294
        $sourceEntityInstance,
295
        ResourceSet $targetResourceSet,
296
        ResourceProperty $targetProperty
297
    ) {
298
        $source = $this->unpackSourceEntity($sourceEntityInstance);
299 1
300 1
        $result = $this->getReader()->getRelatedResourceReference(
301
            $sourceResourceSet,
302 1
            $source,
303
            $targetResourceSet,
304 1
            $targetProperty
305
        );
306 1
        return $result;
307
    }
308
309 1
    /**
310
     * Updates a resource.
311
     *
312
     * @param ResourceSet   $sourceResourceSet    The entity set containing the source entity
313
     * @param object        $sourceEntityInstance The source entity instance
314
     * @param KeyDescriptor $keyDescriptor        The key identifying the entity to fetch
315
     * @param object        $data                 the New data for the entity instance
316
     * @param bool          $shouldUpdate         Should undefined values be updated or reset to default
317
     *
318 1
     * @return Model|null the new resource value if it is assignable or throw exception for null
319
     */
320
    public function updateResource(
321
        ResourceSet $sourceResourceSet,
322 1
        $sourceEntityInstance,
323 1
        KeyDescriptor $keyDescriptor,
324 1
        $data,
325 1
        $shouldUpdate = false
326 1
    ) {
327
        $verb = 'update';
328 1
        return $this->createUpdateMainWrapper($sourceResourceSet, $sourceEntityInstance, $data, $verb);
329
    }
330 1
    /**
331 1
     * Delete resource from a resource set.
332
     *
333
     * @param ResourceSet $sourceResourceSet
334 1
     * @param object      $sourceEntityInstance
335
     *
336
     * return bool true if resources sucessfully deteled, otherwise false
337
     */
338
    public function deleteResource(
339
        ResourceSet $sourceResourceSet,
340
        $sourceEntityInstance
341
    ) {
342
        $source = $this->unpackSourceEntity($sourceEntityInstance);
343 1
344
        $verb = 'delete';
345
        if (!($source instanceof Model)) {
346
            throw new InvalidArgumentException('Source entity must be an Eloquent model.');
347
        }
348 1
349 1
        $class = $sourceResourceSet->getResourceType()->getInstanceType()->getName();
350
        $id = $source->getKey();
351 1
        $name = $source->getKeyName();
352
        $data = [$name => $id];
353 1
354
        $data = $this->createUpdateDeleteCore($source, $data, $class, $verb);
355 1
356
        $success = isset($data['id']);
357
        if ($success) {
358 1
            return true;
359
        }
360
        throw new ODataException('Target model not successfully deleted', 422);
361
    }
362
    /**
363
     * @param ResourceSet $resourceSet          The entity set containing the entity to fetch
364
     * @param object      $sourceEntityInstance The source entity instance
365
     * @param object      $data                 the New data for the entity instance
366
     *
367
     * @returns Model|null                      returns the newly created model if successful,
368
     *                                          or null if model creation failed.
369
     */
370 3
    public function createResourceforResourceSet(
371
        ResourceSet $resourceSet,
372 3
        $sourceEntityInstance,
373 3
        $data
374
    ) {
375 3
        $verb = 'create';
376
        return $this->createUpdateMainWrapper($resourceSet, $sourceEntityInstance, $data, $verb);
377
    }
378 3
379 3
    /**
380
     * @param $sourceEntityInstance
381
     * @param $data
382
     * @param $class
383
     * @param string $verb
384
     *
385 3
     * @throws ODataException
386
     * @throws InvalidOperationException
387
     * @return array|mixed
388 3
     */
389 2
    private function createUpdateDeleteCore($sourceEntityInstance, $data, $class, $verb)
390 2
    {
391 3
        $raw = App::make('metadataControllers');
392
        $map = $raw->getMetadata();
393
394
        if (!array_key_exists($class, $map)) {
395
            throw new \POData\Common\InvalidOperationException('Controller mapping missing for class ' . $class . '.');
396
        }
397 3
        $goal = $raw->getMapping($class, $verb);
398 3
        if (null == $goal) {
399 3
            throw new \POData\Common\InvalidOperationException(
400 3
                'Controller mapping missing for ' . $verb . ' verb on class ' . $class . '.'
401 3
            );
402
        }
403 3
404 3
        if (null === $data) {
405 3
            $msg = 'Data must not be null';
406 3
            throw new InvalidOperationException($msg);
407 2
        }
408 2
        if (is_object($data)) {
409 2
            $arrayData = (array) $data;
410 2
        } else {
411 2
            $arrayData = $data;
412
        }
413
        if (!is_array($arrayData)) {
414
            throw \POData\Common\ODataException::createPreConditionFailedError(
415 2
                'Data not resolvable to key-value array.'
416
            );
417
        }
418 3
419 3
        $controlClass = $goal['controller'];
420
        $method = $goal['method'];
421 3
        $paramList = $goal['parameters'];
422
        $controller = App::make($controlClass);
423 3
        $parms = $this->createUpdateDeleteProcessInput($arrayData, $paramList, $sourceEntityInstance);
424
        unset($data);
425
426 3
        $result = call_user_func_array(array($controller, $method), $parms);
427 3
428 3
        return $this->createUpdateDeleteProcessOutput($result);
429 3
    }
430
431 3
    /**
432
     * Puts an entity instance to entity set identified by a key.
433
     *
434 3
     * @param ResourceSet   $resourceSet   The entity set containing the entity to update
435
     * @param KeyDescriptor $keyDescriptor The key identifying the entity to update
436
     * @param $data
437
     *
438
     * @return bool|null Returns result of executing query
439 3
     */
440
    public function putResource(
441
        ResourceSet $resourceSet,
442
        KeyDescriptor $keyDescriptor,
443
        $data
444
    ) {
445
        // 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...
446
        return true;
447
    }
448
449
    /**
450
     * @param ResourceSet $sourceResourceSet
451
     * @param $data
452
     * @param                            $verb
453
     * @param  Model|null                $source
454
     * @throws InvalidOperationException
455
     * @throws ODataException
456
     * @return Model|null
457
     */
458
    protected function createUpdateCoreWrapper(ResourceSet $sourceResourceSet, $data, $verb, Model $source = null)
459
    {
460
        $lastWord = 'update' == $verb ? 'updated' : 'created';
461
        $class = $sourceResourceSet->getResourceType()->getInstanceType()->getName();
462
        if (!$this->auth->canAuth($this->getVerbMap()[$verb], $class, $source)) {
463
            throw new ODataException('Access denied', 403);
464
        }
465
466
        $payload = $this->createUpdateDeleteCore($source, $data, $class, $verb);
467
468
        $success = isset($payload['id']);
469
470
        if ($success) {
471
            try {
472
                return $class::findOrFail($payload['id']);
473
            } catch (\Exception $e) {
474
                throw new ODataException($e->getMessage(), 500);
475
            }
476
        }
477
        throw new ODataException('Target model not successfully ' . $lastWord, 422);
478
    }
479
480
    /**
481
     * @param $data
482
     * @param $paramList
483
     * @param  Model|null $sourceEntityInstance
484
     * @return array
485
     */
486
    protected function createUpdateDeleteProcessInput($data, $paramList, Model $sourceEntityInstance = null)
487
    {
488
        $parms = [];
489
490
        foreach ($paramList as $spec) {
491
            $varType = isset($spec['type']) ? $spec['type'] : null;
492
            $varName = $spec['name'];
493
            if (null == $varType) {
494
                $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

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

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

697
        $result = $this->createUpdateCoreWrapper($resourceSet, $data, $verb, /** @scrutinizer ignore-type */ $source);
Loading history...
698
        self::queueModel($result);
699
        return $result;
700
    }
701
}
702