Skip to content

Commit 55abb12

Browse files
committed
feature: basic batch crud operations for relation resources
1 parent cb4546a commit 55abb12

File tree

3 files changed

+366
-2
lines changed

3 files changed

+366
-2
lines changed
Lines changed: 363 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,363 @@
1+
<?php
2+
3+
namespace Orion\Concerns;
4+
5+
use Exception;
6+
use Illuminate\Database\Eloquent\Model;
7+
use Illuminate\Database\Eloquent\Relations\BelongsTo;
8+
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
9+
use Illuminate\Database\Eloquent\Relations\MorphToMany;
10+
use Illuminate\Support\Arr;
11+
use Illuminate\Support\Collection;
12+
use Orion\Http\Requests\Request;
13+
use Orion\Http\Resources\CollectionResource;
14+
15+
trait HandlesRelationStandardBatchOperations
16+
{
17+
/**
18+
* Create a batch of new relation resources.
19+
*
20+
* @param Request $request
21+
* @param int|string $parentKey
22+
* @return CollectionResource
23+
*/
24+
public function batchStore(Request $request, $parentKey)
25+
{
26+
$beforeHookResult = $this->beforeBatchStore($request);
27+
if ($this->hookResponds($beforeHookResult)) {
28+
return $beforeHookResult;
29+
}
30+
31+
$resourceModelClass = $this->resolveResourceModelClass();
32+
33+
if ($this->authorizationRequired()) {
34+
$this->authorize('create', $resourceModelClass);
35+
}
36+
37+
/**
38+
* @var Model $entity
39+
*/
40+
$parentEntity = $this->queryBuilder->buildQuery($this->newModelQuery(), $request)
41+
->findOrFail($parentKey);
42+
43+
$resources = $request->get('resources', []);
44+
$entities = collect([]);
45+
46+
foreach ($resources as $resource) {
47+
$entity = new $resourceModelClass;
48+
$entity->fill(Arr::only($resource, $entity->getFillable()));
49+
50+
$this->beforeStore($request, $entity);
51+
$this->beforeSave($request, $entity);
52+
53+
if (!$parentEntity->{$this->getRelation()}() instanceof BelongsTo) {
54+
$parentEntity->{$this->getRelation()}()->save($entity, $this->preparePivotFields(Arr::get($resource, 'pivot', [])));
55+
} else {
56+
$entity->save();
57+
$parentEntity->{$this->getRelation()}()->associate($entity);
58+
}
59+
60+
$entity = $entity->fresh($this->relationsResolver->requestedRelations($request));
61+
$entity->wasRecentlyCreated = true;
62+
63+
$entity = $this->cleanupEntity($entity);
64+
65+
if (count($this->getPivotJson())) {
66+
$entity = $this->castPivotJsonFields($entity);
67+
}
68+
69+
$this->afterSave($request, $entity);
70+
$this->afterStore($request, $entity);
71+
72+
$entities->push($entity);
73+
}
74+
75+
$afterHookResult = $this->afterBatchStore($request, $entities);
76+
if ($this->hookResponds($afterHookResult)) {
77+
return $afterHookResult;
78+
}
79+
80+
return $this->collectionResponse($entities);
81+
}
82+
83+
/**
84+
* Updates a batch of relation resources.
85+
*
86+
* @param Request $request
87+
* @param int|string $parentKey
88+
* @return CollectionResource
89+
*/
90+
public function batchUpdate(Request $request, $parentKey)
91+
{
92+
$beforeHookResult = $this->beforeBatchUpdate($request);
93+
if ($this->hookResponds($beforeHookResult)) {
94+
return $beforeHookResult;
95+
}
96+
97+
$parentEntity = $this->queryBuilder->buildQuery($this->newModelQuery(), $request)
98+
->findOrFail($parentKey);
99+
100+
$resourceModelClass = $this->resolveResourceModelClass();
101+
$resourceKeyName = (new $resourceModelClass)->getKeyName();
102+
103+
$entities = $this->relationQueryBuilder->buildQuery($this->newRelationQuery($parentEntity), $request)
104+
->with($this->relationsResolver->requestedRelations($request))
105+
->whereIn($resourceKeyName, array_keys($request->get('resources', [])))
106+
->get();
107+
108+
foreach ($entities as $entity) {
109+
/**
110+
* @var Model $entity
111+
*/
112+
if ($this->authorizationRequired()) {
113+
$this->authorize('update', $entity);
114+
}
115+
116+
$resource = $request->input("resources.{$entity->getKey()}");
117+
118+
$entity->fill(Arr::only($resource, $entity->getFillable()));
119+
120+
$this->beforeUpdate($request, $entity);
121+
$this->beforeSave($request, $entity);
122+
123+
$entity->save();
124+
125+
$relation = $parentEntity->{$this->getRelation()}();
126+
if ($relation instanceof BelongsToMany || $relation instanceof MorphToMany) {
127+
$relation->updateExistingPivot($entity->getKey(), $this->preparePivotFields(Arr::get($resource, 'pivot', [])));
128+
129+
$entity = $entity->fresh($this->relationsResolver->requestedRelations($request));
130+
}
131+
132+
$entity = $this->cleanupEntity($entity);
133+
134+
if (count($this->getPivotJson())) {
135+
$entity = $this->castPivotJsonFields($entity);
136+
}
137+
138+
$this->afterSave($request, $entity);
139+
$this->afterUpdate($request, $entity);
140+
}
141+
142+
$afterHookResult = $this->afterBatchUpdate($request, $entities);
143+
if ($this->hookResponds($afterHookResult)) {
144+
return $afterHookResult;
145+
}
146+
147+
return $this->collectionResponse($entities);
148+
}
149+
150+
/**
151+
* Deletes a batch of relation resources.
152+
*
153+
* @param Request $request
154+
* @param int|string $parentKey
155+
* @return CollectionResource
156+
* @throws Exception
157+
*/
158+
public function batchDestroy(Request $request, $parentKey)
159+
{
160+
$beforeHookResult = $this->beforeBatchDestroy($request);
161+
if ($this->hookResponds($beforeHookResult)) {
162+
return $beforeHookResult;
163+
}
164+
165+
$parentEntity = $this->queryBuilder->buildQuery($this->newModelQuery(), $request)
166+
->findOrFail($parentKey);
167+
168+
$resourceModelClass = $this->resolveResourceModelClass();
169+
$resourceKeyName = (new $resourceModelClass)->getKeyName();
170+
171+
$softDeletes = $this->softDeletes($this->resolveResourceModelClass());
172+
173+
$entities = $this->relationQueryBuilder->buildQuery($this->newRelationQuery($parentEntity), $request)
174+
->with($this->relationsResolver->requestedRelations($request))
175+
->when($softDeletes, function ($query) {
176+
$query->withTrashed();
177+
})
178+
->whereIn($resourceKeyName, $request->get('resources', []))
179+
->get();
180+
181+
foreach ($entities as $entity) {
182+
/**
183+
* @var Model $entity
184+
*/
185+
$forceDeletes = $softDeletes && $request->get('force');
186+
187+
if ($this->authorizationRequired()) {
188+
$this->authorize($forceDeletes ? 'forceDelete' : 'delete', $entity);
189+
}
190+
191+
$this->beforeDestroy($request, $entity);
192+
193+
if (!$forceDeletes) {
194+
$entity->delete();
195+
} else {
196+
$entity->forceDelete();
197+
}
198+
199+
$entity = $this->cleanupEntity($entity);
200+
201+
if (count($this->getPivotJson())) {
202+
$entity = $this->castPivotJsonFields($entity);
203+
}
204+
205+
$this->afterDestroy($request, $entity);
206+
}
207+
208+
$afterHookResult = $this->afterBatchDestroy($request, $entities);
209+
if ($this->hookResponds($afterHookResult)) {
210+
return $afterHookResult;
211+
}
212+
213+
return $this->collectionResponse($entities);
214+
}
215+
216+
/**
217+
* Restores a batch of relation resources.
218+
*
219+
* @param Request $request
220+
* @param int|string $parentKey
221+
* @return CollectionResource
222+
* @throws Exception
223+
*/
224+
public function batchRestore(Request $request, $parentKey)
225+
{
226+
$beforeHookResult = $this->beforeBatchRestore($request);
227+
if ($this->hookResponds($beforeHookResult)) {
228+
return $beforeHookResult;
229+
}
230+
231+
$parentEntity = $this->queryBuilder->buildQuery($this->newModelQuery(), $request)
232+
->findOrFail($parentKey);
233+
234+
$resourceModelClass = $this->resolveResourceModelClass();
235+
$resourceKeyName = (new $resourceModelClass)->getKeyName();
236+
237+
$entities = $this->relationQueryBuilder->buildQuery($this->newRelationQuery($parentEntity), $request)
238+
->with($this->relationsResolver->requestedRelations($request))
239+
->whereIn($resourceKeyName, $request->get('resources', []))
240+
->withTrashed()
241+
->get();
242+
243+
foreach ($entities as $entity) {
244+
/**
245+
* @var Model $entity
246+
*/
247+
if ($this->authorizationRequired()) {
248+
$this->authorize('restore', $entity);
249+
}
250+
251+
$this->beforeRestore($request, $entity);
252+
253+
$entity->restore();
254+
255+
$entity = $this->cleanupEntity($entity);
256+
257+
if (count($this->getPivotJson())) {
258+
$entity = $this->castPivotJsonFields($entity);
259+
}
260+
261+
$this->afterRestore($request, $entity);
262+
}
263+
264+
$afterHookResult = $this->afterBatchRestore($request, $entities);
265+
if ($this->hookResponds($afterHookResult)) {
266+
return $afterHookResult;
267+
}
268+
269+
return $this->collectionResponse($entities);
270+
}
271+
272+
/**
273+
* The hook is executed before creating a batch of new resources.
274+
*
275+
* @param Request $request
276+
* @return mixed
277+
*/
278+
protected function beforeBatchStore(Request $request)
279+
{
280+
return null;
281+
}
282+
283+
/**
284+
* The hook is executed after creating a batch of new resources.
285+
*
286+
* @param Request $request
287+
* @param Collection $entities
288+
* @return mixed
289+
*/
290+
protected function afterBatchStore(Request $request, Collection $entities)
291+
{
292+
return null;
293+
}
294+
295+
/**
296+
* The hook is executed before updating a batch of resources.
297+
*
298+
* @param Request $request
299+
* @return mixed
300+
*/
301+
protected function beforeBatchUpdate(Request $request)
302+
{
303+
return null;
304+
}
305+
306+
/**
307+
* The hook is executed after updating a batch of resources.
308+
*
309+
* @param Request $request
310+
* @param Collection $entities
311+
* @return mixed
312+
*/
313+
protected function afterBatchUpdate(Request $request, Collection $entities)
314+
{
315+
return null;
316+
}
317+
318+
/**
319+
* The hook is executed before deleting a batch of resources.
320+
*
321+
* @param Request $request
322+
* @return mixed
323+
*/
324+
protected function beforeBatchDestroy(Request $request)
325+
{
326+
return null;
327+
}
328+
329+
/**
330+
* The hook is executed after deleting a batch of resources.
331+
*
332+
* @param Request $request
333+
* @param Collection $entities
334+
* @return mixed
335+
*/
336+
protected function afterBatchDestroy(Request $request, Collection $entities)
337+
{
338+
return null;
339+
}
340+
341+
/**
342+
* The hook is executed before restoring a batch of resources.
343+
*
344+
* @param Request $request
345+
* @return mixed
346+
*/
347+
protected function beforeBatchRestore(Request $request)
348+
{
349+
return null;
350+
}
351+
352+
/**
353+
* The hook is executed after restoring a batch of resources.
354+
*
355+
* @param Request $request
356+
* @param Collection $entities
357+
* @return mixed
358+
*/
359+
protected function afterBatchRestore(Request $request, Collection $entities)
360+
{
361+
return null;
362+
}
363+
}

src/Concerns/HandlesStandardBatchOperations.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -220,7 +220,7 @@ protected function beforeBatchStore(Request $request)
220220
}
221221

222222
/**
223-
* The hook is executed after creating a batch of new resource.
223+
* The hook is executed after creating a batch of new resources.
224224
*
225225
* @param Request $request
226226
* @param Collection $entities

src/Http/Controllers/RelationController.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,14 @@
88
use Illuminate\Support\Facades\App;
99
use Orion\Concerns\HandlesRelationManyToManyOperations;
1010
use Orion\Concerns\HandlesRelationOneToManyOperations;
11+
use Orion\Concerns\HandlesRelationStandardBatchOperations;
1112
use Orion\Concerns\HandlesRelationStandardOperations;
1213
use Orion\Contracts\QueryBuilder;
1314
use Orion\Exceptions\BindingException;
1415

1516
abstract class RelationController extends BaseController
1617
{
17-
use HandlesRelationStandardOperations, HandlesRelationOneToManyOperations, HandlesRelationManyToManyOperations;
18+
use HandlesRelationStandardOperations, HandlesRelationStandardBatchOperations, HandlesRelationOneToManyOperations, HandlesRelationManyToManyOperations;
1819

1920
/**
2021
* @var string $relation

0 commit comments

Comments
 (0)