Passed
Pull Request — master (#155)
by Alex
06:39
created

LaravelQuery::isModelHookInputsOk()   B

Complexity

Conditions 5
Paths 4

Size

Total Lines 17
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 30

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 17
ccs 0
cts 0
cp 0
rs 8.8571
cc 5
eloc 12
nc 4
nop 3
crap 30
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
42
    public function __construct(AuthInterface $auth = null)
43
    {
44
        /* MySQLExpressionProvider();*/
45
        $this->expression = new LaravelExpressionProvider(); //PHPExpressionProvider('expression');
46
        $this->queryProviderClassName = get_class($this);
47
        $this->auth = isset($auth) ? $auth : new NullAuthProvider();
48
        $this->reader = new LaravelReadQuery($this->auth);
49
        $this->modelHook = new LaravelHookQuery($this->auth);
50
        $this->bulk = new LaravelBulkQuery($this, $this->auth);
51
        $this->metadataProvider = new MetadataProvider(App::make('app'));
52
        $this->controllerContainer = App::make('metadataControllers');
53
    }
54
55
    /**
56
     * Indicates if the QueryProvider can handle ordered paging, this means respecting order, skip, and top parameters
57
     * If the query provider can not handle ordered paging, it must return the entire result set and POData will
58
     * perform the ordering and paging.
59
     *
60
     * @return Boolean True if the query provider can handle ordered paging, false if POData should perform the paging
61
     */
62
    public function handlesOrderedPaging()
63
    {
64
        return true;
65
    }
66
67
    /**
68
     * Gets the expression provider used by to compile OData expressions into expression used by this query provider.
69
     *
70 3
     * @return \POData\Providers\Expression\IExpressionProvider
71
     */
72
    public function getExpressionProvider()
73
    {
74
        return $this->expression;
75
    }
76
77
    /**
78
     * Gets the LaravelReadQuery instance used to handle read queries (repetitious, nyet?).
79 3
     *
80
     * @return LaravelReadQuery
81
     */
82 3
    public function getReader()
83 1
    {
84 1
        return $this->reader;
85
    }
86 3
87 3
    /**
88 3
     * Gets the LaravelHookQuery instance used to handle hook/unhook queries (repetitious, nyet?).
89
     *
90 3
     * @return LaravelHookQuery
91
     */
92
    public function getModelHook()
93
    {
94
        return $this->modelHook;
95
    }
96
97 1
    /**
98
     * Gets the LaravelBulkQuery instance used to handle bulk queries (repetitious, nyet?).
99
     *
100 3
     * @return LaravelBulkQuery
101 1
     */
102 1
    public function getBulk()
103 3
    {
104 1
        return $this->bulk;
105 1
    }
106
107 3
    /**
108
     * Dig out local copy of POData-Laravel metadata provider.
109 3
     *
110
     * @return MetadataProvider
111
     */
112
    public function getMetadataProvider()
113
    {
114
        return $this->metadataProvider;
115
    }
116
117 3
    /**
118 1
     * Dig out local copy of controller metadata mapping.
119 1
     *
120 1
     * @return MetadataControllerContainer
121 1
     */
122 1
    public function getControllerContainer()
123 3
    {
124 3
        assert(null !== $this->controllerContainer, get_class($this->controllerContainer));
125
        return $this->controllerContainer;
126
    }
127 3
128 3
    public function getVerbMap()
129 3
    {
130
        if (0 == count($this->verbMap)) {
131
            $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

131
            /** @scrutinizer ignore-call */ 
132
            $this->verbMap['create'] = ActionVerb::CREATE();
Loading history...
132
            $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

132
            /** @scrutinizer ignore-call */ 
133
            $this->verbMap['update'] = ActionVerb::UPDATE();
Loading history...
133
            $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

133
            /** @scrutinizer ignore-call */ 
134
            $this->verbMap['delete'] = ActionVerb::DELETE();
Loading history...
134
        }
135
        return $this->verbMap;
136
    }
137
138
    /**
139
     * Gets collection of entities belongs to an entity set
140
     * IE: http://host/EntitySet
141
     *  http://host/EntitySet?$skip=10&$top=5&filter=Prop gt Value.
142
     *
143
     * @param QueryType                $queryType            Is this is a query for a count, entities,
144
     *                                                       or entities-with-count?
145
     * @param ResourceSet              $resourceSet          The entity set containing the entities to fetch
146
     * @param FilterInfo|null          $filterInfo           The $filter parameter of the OData query.  NULL if absent
147
     * @param null|InternalOrderByInfo $orderBy              sorted order if we want to get the data in some
148
     *                                                       specific order
149
     * @param int|null                 $top                  number of records which need to be retrieved
150
     * @param int|null                 $skip                 number of records which need to be skipped
151
     * @param SkipTokenInfo|null       $skipToken            value indicating what records to skip
152
     * @param string[]|null            $eagerLoad            array of relations to eager load
153
     * @param Model|Relation|null      $sourceEntityInstance Starting point of query
154
     *
155
     * @return QueryResult
156
     */
157
    public function getResourceSet(
158
        QueryType $queryType,
159
        ResourceSet $resourceSet,
160
        $filterInfo = null,
161
        $orderBy = null,
162
        $top = null,
163
        $skip = null,
164
        $skipToken = null,
165
        array $eagerLoad = null,
166
        $sourceEntityInstance = null
167
    ) {
168
        $source = $this->unpackSourceEntity($sourceEntityInstance);
169
        return $this->getReader()->getResourceSet(
170
            $queryType,
171
            $resourceSet,
172
            $filterInfo,
173
            $orderBy,
174
            $top,
175
            $skip,
176
            $skipToken,
177
            $eagerLoad,
178
            $source
179
        );
180
    }
181
    /**
182
     * Gets an entity instance from an entity set identified by a key
183
     * IE: http://host/EntitySet(1L)
184
     * http://host/EntitySet(KeyA=2L,KeyB='someValue').
185
     *
186
     * @param ResourceSet        $resourceSet   The entity set containing the entity to fetch
187
     * @param KeyDescriptor|null $keyDescriptor The key identifying the entity to fetch
188
     * @param string[]|null      $eagerLoad     array of relations to eager load
189
     *
190
     * @return Model|null Returns entity instance if found else null
191
     */
192
    public function getResourceFromResourceSet(
193
        ResourceSet $resourceSet,
194
        KeyDescriptor $keyDescriptor = null,
195
        array $eagerLoad = null
196
    ) {
197
        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...
198
    }
199 3
200
    /**
201
     * Get related resource set for a resource
202
     * IE: http://host/EntitySet(1L)/NavigationPropertyToCollection
203
     * http://host/EntitySet?$expand=NavigationPropertyToCollection.
204
     *
205
     * @param QueryType          $queryType            Is this is a query for a count, entities, or entities-with-count
206
     * @param ResourceSet        $sourceResourceSet    The entity set containing the source entity
207
     * @param object             $sourceEntityInstance The source entity instance
208
     * @param ResourceSet        $targetResourceSet    The resource set pointed to by the navigation property
209
     * @param ResourceProperty   $targetProperty       The navigation property to retrieve
210 3
     * @param FilterInfo|null    $filter               The $filter parameter of the OData query.  NULL if none specified
211 3
     * @param mixed|null         $orderBy              sorted order if we want to get the data in some specific order
212
     * @param int|null           $top                  number of records which need to be retrieved
213 3
     * @param int|null           $skip                 number of records which need to be skipped
214 3
     * @param SkipTokenInfo|null $skipToken            value indicating what records to skip
215 3
     *
216 3
     * @return QueryResult
217 3
     */
218 3
    public function getRelatedResourceSet(
219 3
        QueryType $queryType,
220
        ResourceSet $sourceResourceSet,
221 3
        $sourceEntityInstance,
222
        ResourceSet $targetResourceSet,
223
        ResourceProperty $targetProperty,
224
        FilterInfo $filter = null,
225
        $orderBy = null,
226
        $top = null,
227
        $skip = null,
228
        $skipToken = null
229
    ) {
230
        $source = $this->unpackSourceEntity($sourceEntityInstance);
231
        return $this->getReader()->getRelatedResourceSet(
232
            $queryType,
233
            $sourceResourceSet,
234
            $source,
235
            $targetResourceSet,
236
            $targetProperty,
237
            $filter,
238
            $orderBy,
239
            $top,
240
            $skip,
241
            $skipToken
242
        );
243
    }
244
245
    /**
246
     * Gets a related entity instance from an entity set identified by a key
247
     * IE: http://host/EntitySet(1L)/NavigationPropertyToCollection(33).
248
     *
249
     * @param ResourceSet      $sourceResourceSet    The entity set containing the source entity
250
     * @param object           $sourceEntityInstance the source entity instance
251
     * @param ResourceSet      $targetResourceSet    The entity set containing the entity to fetch
252
     * @param ResourceProperty $targetProperty       the metadata of the target property
253
     * @param KeyDescriptor    $keyDescriptor        The key identifying the entity to fetch
254
     *
255
     * @return Model|null Returns entity instance if found else null
256
     */
257
    public function getResourceFromRelatedResourceSet(
258
        ResourceSet $sourceResourceSet,
259
        $sourceEntityInstance,
260
        ResourceSet $targetResourceSet,
261
        ResourceProperty $targetProperty,
262
        KeyDescriptor $keyDescriptor
263
    ) {
264
        $source = $this->unpackSourceEntity($sourceEntityInstance);
265
        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...
266
            $sourceResourceSet,
267
            $source,
268
            $targetResourceSet,
269
            $targetProperty,
270
            $keyDescriptor
271
        );
272
    }
273
274
    /**
275
     * Get related resource for a resource
276
     * IE: http://host/EntitySet(1L)/NavigationPropertyToSingleEntity
277
     * http://host/EntitySet?$expand=NavigationPropertyToSingleEntity.
278
     *
279
     * @param ResourceSet      $sourceResourceSet    The entity set containing the source entity
280
     * @param object           $sourceEntityInstance the source entity instance
281
     * @param ResourceSet      $targetResourceSet    The entity set containing the entity pointed to by the nav property
282
     * @param ResourceProperty $targetProperty       The navigation property to fetch
283
     *
284
     * @return object|null The related resource if found else null
285
     */
286
    public function getRelatedResourceReference(
287
        ResourceSet $sourceResourceSet,
288
        $sourceEntityInstance,
289
        ResourceSet $targetResourceSet,
290
        ResourceProperty $targetProperty
291
    ) {
292 1
        $source = $this->unpackSourceEntity($sourceEntityInstance);
293
294
        $result = $this->getReader()->getRelatedResourceReference(
295
            $sourceResourceSet,
296
            $source,
297
            $targetResourceSet,
298
            $targetProperty
299 1
        );
300 1
        return $result;
301
    }
302 1
303
    /**
304 1
     * Updates a resource.
305
     *
306 1
     * @param ResourceSet   $sourceResourceSet    The entity set containing the source entity
307
     * @param object        $sourceEntityInstance The source entity instance
308
     * @param KeyDescriptor $keyDescriptor        The key identifying the entity to fetch
309 1
     * @param object        $data                 the New data for the entity instance
310
     * @param bool          $shouldUpdate         Should undefined values be updated or reset to default
311
     *
312
     * @return object|null the new resource value if it is assignable or throw exception for null
313
     */
314
    public function updateResource(
315
        ResourceSet $sourceResourceSet,
316
        $sourceEntityInstance,
317
        KeyDescriptor $keyDescriptor,
318 1
        $data,
319
        $shouldUpdate = false
320
    ) {
321
        $source = $this->unpackSourceEntity($sourceEntityInstance);
322 1
323 1
        $verb = 'update';
324 1
        return $this->createUpdateCoreWrapper($sourceResourceSet, $data, $verb, $source);
325 1
    }
326 1
    /**
327
     * Delete resource from a resource set.
328 1
     *
329
     * @param ResourceSet $sourceResourceSet
330 1
     * @param object      $sourceEntityInstance
331 1
     *
332
     * return bool true if resources sucessfully deteled, otherwise false
333
     */
334 1
    public function deleteResource(
335
        ResourceSet $sourceResourceSet,
336
        $sourceEntityInstance
337
    ) {
338
        $source = $this->unpackSourceEntity($sourceEntityInstance);
339
340
        $verb = 'delete';
341
        if (!($source instanceof Model)) {
342
            throw new InvalidArgumentException('Source entity must be an Eloquent model.');
343 1
        }
344
345
        $class = $sourceResourceSet->getResourceType()->getInstanceType()->getName();
346
        $id = $source->getKey();
347
        $name = $source->getKeyName();
348 1
        $data = [$name => $id];
349 1
350
        $data = $this->createUpdateDeleteCore($source, $data, $class, $verb);
351 1
352
        $success = isset($data['id']);
353 1
        if ($success) {
354
            return true;
355 1
        }
356
        throw new ODataException('Target model not successfully deleted', 422);
357
    }
358 1
    /**
359
     * @param ResourceSet $resourceSet          The entity set containing the entity to fetch
360
     * @param object      $sourceEntityInstance The source entity instance
361
     * @param object      $data                 the New data for the entity instance
362
     *
363
     * @returns object|null                     returns the newly created model if successful,
364
     *                                          or null if model creation failed.
365
     */
366
    public function createResourceforResourceSet(
367
        ResourceSet $resourceSet,
368
        $sourceEntityInstance,
369
        $data
370 3
    ) {
371
        $source = $this->unpackSourceEntity($sourceEntityInstance);
372 3
373 3
        $verb = 'create';
374
        return $this->createUpdateCoreWrapper($resourceSet, $data, $verb, $source);
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
    private 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()
646
    {
647
        DB::beginTransaction();
648
    }
649
650
    /**
651
     * Commit database transaction.
652
     */
653
    public function commitTransaction()
654
    {
655
        DB::commit();
656
    }
657
658
    /**
659
     * Abort database transaction.
660
     */
661
    public function rollBackTransaction()
662
    {
663
        DB::rollBack();
664
    }
665
}
666