@@ -29,6 +29,12 @@ trait ResourceControllerTrait
29
29
protected string $ label = '' ;
30
30
protected bool $ simplePaginate = false ;
31
31
protected array $ modelFqnToControllerMap = [];
32
+
33
+ /**
34
+ * If a related resource is not exposed via crud in $modelFqnToControllerMap,
35
+ * it can be exposed in this property
36
+ */
37
+ protected array $ relatedModelFqnToControllerMap = [];
32
38
protected ResourceServiceInterface $ resourceService ;
33
39
protected array $ paginationKeys = [
34
40
'current_page ' ,
@@ -213,6 +219,16 @@ protected function getFilteredRelations(array $relations, ?BaseModel $baseModel
213
219
$ this ->getResourceAsModelFQN ())::WITH_RELATIONS , $ relations );
214
220
}
215
221
222
+ /**
223
+ * @throws \Exception
224
+ */
225
+ protected function validateRelation (string $ relation ): void
226
+ {
227
+ if (!\in_array ($ relation , $ this ->getResourceAsModelFQN ()::WITH_RELATIONS , true )) {
228
+ throw new \Exception ('Relation as subresource not found for this resource. ' );
229
+ }
230
+ }
231
+
216
232
protected function handleList (array $ allRequest , Request $ request ): Response
217
233
{
218
234
try {
@@ -240,6 +256,74 @@ protected function handleList(array $allRequest, Request $request): Response
240
256
}
241
257
}
242
258
259
+ public function getRelated (
260
+ Request $ request ,
261
+ string $ identifier ,
262
+ string $ relation ,
263
+ string $ relatedIdentifier
264
+ ): JsonResponse {
265
+ try {
266
+ return $ this ->getRelatedController (
267
+ $ this ->getRelatedFromRelation ($ identifier , $ relation , $ relatedIdentifier ),
268
+ $ relation
269
+ )->get ($ relatedIdentifier , $ request );
270
+ } catch (\Throwable $ e ) {
271
+ if (!$ e instanceof ModelNotFoundException) {
272
+ Log::error ($ this ->label . '/ ' . $ identifier . '/ ' . $ relation . '/ ' . $ relatedIdentifier .
273
+ ', error = ' . $ e ->getMessage ());
274
+ }
275
+
276
+ return GeneralHelper::app (JsonResponse::class, [
277
+ 'data ' => ['message ' => GeneralHelper::getSafeErrorMessage ($ e )],
278
+ 'status ' => 400
279
+ ]);
280
+ }
281
+ }
282
+
283
+ public function updateRelated (
284
+ Request $ request ,
285
+ string $ identifier ,
286
+ string $ relation ,
287
+ string $ relatedIdentifier
288
+ ): JsonResponse {
289
+ try {
290
+ return $ this ->getRelatedController (
291
+ $ this ->getRelatedFromRelation ($ identifier , $ relation , $ relatedIdentifier ),
292
+ $ relation
293
+ )->update ($ relatedIdentifier , $ request );
294
+ } catch (\Throwable $ e ) {
295
+ if (!$ e instanceof ModelNotFoundException) {
296
+ Log::error ($ this ->label . '/ ' . $ identifier . '/ ' . $ relation . '/ ' . $ relatedIdentifier .
297
+ ', error = ' . $ e ->getMessage ());
298
+ }
299
+
300
+ return GeneralHelper::app (JsonResponse::class, [
301
+ 'data ' => ['message ' => GeneralHelper::getSafeErrorMessage ($ e )],
302
+ 'status ' => 400
303
+ ]);
304
+ }
305
+ }
306
+
307
+ public function deleteRelated (string $ identifier , string $ relation , string $ relatedIdentifier ): JsonResponse
308
+ {
309
+ try {
310
+ return $ this ->getRelatedController (
311
+ $ this ->getRelatedFromRelation ($ identifier , $ relation , $ relatedIdentifier ),
312
+ $ relation
313
+ )->delete ($ relatedIdentifier );
314
+ } catch (\Throwable $ e ) {
315
+ if (!$ e instanceof ModelNotFoundException) {
316
+ Log::error ($ this ->label . '/ ' . $ identifier . '/ ' . $ relation . '/ ' . $ relatedIdentifier .
317
+ ', error = ' . $ e ->getMessage ());
318
+ }
319
+
320
+ return GeneralHelper::app (JsonResponse::class, [
321
+ 'data ' => ['message ' => GeneralHelper::getSafeErrorMessage ($ e )],
322
+ 'status ' => 400
323
+ ]);
324
+ }
325
+ }
326
+
243
327
/**
244
328
* @throws \Exception
245
329
*/
@@ -255,7 +339,6 @@ protected function getResourceAsModelFQN(): string
255
339
throw new \Exception ('Could not getResourceAsModelFQN. ' );
256
340
}
257
341
258
-
259
342
protected function getEmptyPaginatedResponse (array $ request ): JsonResponse
260
343
{
261
344
$ data = [
@@ -379,4 +462,56 @@ protected function throwIfForbidden(bool $condition): void
379
462
throw new \Exception ('Forbidden ' );
380
463
}
381
464
}
465
+
466
+ /**
467
+ * @return self
468
+ * @throws \Throwable
469
+ */
470
+ protected function getRelatedController (BaseModel $ related , string $ relation ): object
471
+ {
472
+ $ controllerFqn = (string )($ this ->modelFqnToControllerMap [$ related ::class] ??
473
+ ($ this ->relatedModelFqnToControllerMap [$ related ::class] ?? '' ));
474
+
475
+ if ('' === $ controllerFqn ) {
476
+ throw new \Exception ('Related ' . $ relation . ' not exposed as resource. ' );
477
+ }
478
+
479
+ return GeneralHelper::app ($ controllerFqn );
480
+ }
481
+
482
+ /**
483
+ * @throws \Throwable
484
+ */
485
+ protected function getRelatedFromRelation (
486
+ string $ identifier ,
487
+ string $ relation ,
488
+ string $ relatedIdentifier
489
+ ): BaseModel {
490
+ $ this ->validateRelation ($ relation );
491
+ /** @var Relation $relationInstance */
492
+ $ relationInstance = $ this ->resourceService ->get ($ identifier , appendIndex: false )->{$ relation }();
493
+ /** @var BaseModel $related */
494
+ $ related = $ relationInstance ->getRelated ();
495
+ $ exploded = \explode ($ related ::COMPOSITE_PK_SEPARATOR , $ relatedIdentifier );
496
+ $ pks = [];
497
+
498
+ foreach ($ related ->getPrimaryKeyFilter () as $ column => $ value ) {
499
+ if (!\is_array ($ value ) && \is_string ($ column )) {
500
+ $ pks [] = $ column ;
501
+
502
+ continue ;
503
+ }
504
+
505
+ $ pks [] = \reset ($ value );
506
+ }
507
+
508
+ if (
509
+ \count ($ pks ) !== \count ($ exploded )
510
+ || !$ relationInstance ->where (\array_combine ($ pks , $ exploded ))->exists ()
511
+ ) {
512
+ throw (new ModelNotFoundException ())->setModel ($ related ::class);
513
+ }
514
+
515
+ return $ related ;
516
+ }
382
517
}
0 commit comments