24
24
use Illuminate \Database \Eloquent \Model ;
25
25
use Illuminate \Http \Response ;
26
26
use LaravelJsonApi \Contracts \Auth \Authorizer ;
27
- use LaravelJsonApi \Contracts \Resources \ Container as ResourceContainer ;
27
+ use LaravelJsonApi \Contracts \Schema \ Relation ;
28
28
use LaravelJsonApi \Core \Document \ResourceObject ;
29
29
use LaravelJsonApi \Core \Exceptions \JsonApiException ;
30
- use LaravelJsonApi \Core \Resources \JsonApiResource ;
31
- use LaravelJsonApi \Core \Resources \Relation ;
30
+ use LaravelJsonApi \Core \Query \IncludePaths ;
32
31
use LaravelJsonApi \Core \Support \Str ;
33
32
use LaravelJsonApi \Spec \RelationBuilder ;
34
33
use LaravelJsonApi \Spec \ResourceBuilder ;
41
40
class ResourceRequest extends FormRequest
42
41
{
43
42
44
- /**
45
- * @var bool
46
- */
47
- protected bool $ validateExisting = true ;
48
-
49
43
/**
50
44
* @var callable|null
51
45
*/
@@ -353,43 +347,37 @@ protected function dataForCreate(array $document): array
353
347
* @param Model|object $model
354
348
* the model being updated.
355
349
* @param array $document
356
- * the JSON API document to validate.
350
+ * the JSON: API document to validate.
357
351
* @return array
358
352
*/
359
353
protected function dataForUpdate (object $ model , array $ document ): array
360
354
{
361
- $ data = $ document ['data ' ] ?? [];
362
-
363
- if ($ this ->mustValidateExisting ($ model , $ document )) {
364
- $ data ['attributes ' ] = $ this ->extractAttributes (
365
- $ model ,
366
- $ data ['attributes ' ] ?? []
367
- );
355
+ $ existing = $ this ->extractForUpdate ($ model );
368
356
369
- $ data ['relationships ' ] = $ this ->extractRelationships (
370
- $ model ,
371
- $ data ['relationships ' ] ?? []
372
- );
357
+ if (method_exists ($ this , 'withExisting ' )) {
358
+ $ existing = $ this ->withExisting ($ model , $ existing ) ?? $ existing ;
373
359
}
374
360
375
- return $ data ;
361
+ return ResourceObject::fromArray ($ existing )->merge (
362
+ $ document ['data ' ]
363
+ )->jsonSerialize ();
376
364
}
377
365
378
366
/**
379
367
* Get validation data for modifying a relationship.
380
368
*
381
- * @param Model|object $record
369
+ * @param Model|object $model
382
370
* @param string $fieldName
383
371
* @param array $document
384
372
* @return array
385
373
*/
386
- protected function dataForRelationship (object $ record , string $ fieldName , array $ document ): array
374
+ protected function dataForRelationship (object $ model , string $ fieldName , array $ document ): array
387
375
{
388
- $ resource = $ this ->resources ()->create ( $ record );
376
+ $ route = $ this ->jsonApi ()->route ( );
389
377
390
378
return [
391
- 'type ' => $ resource -> type (),
392
- 'id ' => $ resource -> id (),
379
+ 'type ' => $ route -> resourceType (),
380
+ 'id ' => $ route -> resourceId (),
393
381
'relationships ' => [
394
382
$ fieldName => [
395
383
'data ' => $ document ['data ' ],
@@ -406,14 +394,7 @@ protected function dataForRelationship(object $record, string $fieldName, array
406
394
*/
407
395
protected function dataForDelete (object $ record ): array
408
396
{
409
- $ route = $ this ->jsonApi ()->route ();
410
-
411
- $ data = $ this ->dataForUpdate ($ record , [
412
- 'data ' => [
413
- 'type ' => $ route ->resourceType (),
414
- 'id ' => $ route ->resourceId (),
415
- ],
416
- ]);
397
+ $ data = $ this ->extractForUpdate ($ record );
417
398
418
399
if (method_exists ($ this , 'metaForDelete ' )) {
419
400
$ data ['meta ' ] = (array ) $ this ->metaForDelete ($ record );
@@ -423,80 +404,36 @@ protected function dataForDelete(object $record): array
423
404
}
424
405
425
406
/**
426
- * Should existing resource values be provided to the validator for an update request?
407
+ * Extract the existing values for the provided model.
427
408
*
428
- * Child classes can overload this method if they need to programmatically work out
429
- * if existing values must be provided to the validator instance for an update request.
430
- *
431
- * @param Model|object $model
432
- * the model being updated
433
- * @param array $document
434
- * the JSON API document provided by the client.
435
- * @return bool
436
- */
437
- protected function mustValidateExisting (object $ model , array $ document ): bool
438
- {
439
- return false !== $ this ->validateExisting ;
440
- }
441
-
442
- /**
443
- * Extract existing attributes for the provided model.
444
- *
445
- * @param Model|object $model
446
- * @return iterable
409
+ * @param object $model
410
+ * @return array
447
411
*/
448
- protected function existingAttributes (object $ model ): iterable
412
+ private function extractForUpdate (object $ model ): array
449
413
{
450
- $ resource = $ this ->resources ()->create ( $ model );
414
+ $ encoder = $ this ->jsonApi ()->server ()-> encoder ( );
451
415
452
- return $ resource ->attributes ($ this );
416
+ return $ encoder
417
+ ->withRequest ($ this )
418
+ ->withIncludePaths ($ this ->includePathsToExtract ($ model ))
419
+ ->withResource ($ model )
420
+ ->toArray ()['data ' ];
453
421
}
454
422
455
423
/**
456
- * Extract existing relationships for the provided model .
424
+ * Get the relationship paths that must be included when extracting the current field values .
457
425
*
458
- * @param Model| object $model
459
- * @return iterable
426
+ * @param object $model
427
+ * @return IncludePaths
460
428
*/
461
- protected function existingRelationships (object $ model ): iterable
429
+ private function includePathsToExtract (object $ model ): IncludePaths
462
430
{
463
- $ resource = $ this ->resources ()->create ($ model );
464
-
465
- /** @var Relation $relationship */
466
- foreach ($ resource ->relationships ($ this ) as $ relationship ) {
467
- if ($ relationship ->isValidated ()) {
468
- yield $ relationship ->fieldName () => $ relationship ->data ();
469
- }
470
- }
471
- }
431
+ $ paths = collect ($ this ->schema ()->relationships ())
432
+ ->filter (fn (Relation $ relation ) => $ relation ->isValidated ())
433
+ ->map (fn (Relation $ relation ) => $ relation ->name ())
434
+ ->values ();
472
435
473
- /**
474
- * Extract attributes for a resource update.
475
- *
476
- * @param Model|object $model
477
- * @param array $new
478
- * @return array
479
- */
480
- private function extractAttributes (object $ model , array $ new ): array
481
- {
482
- return collect ($ this ->existingAttributes ($ model ))
483
- ->merge ($ new )
484
- ->all ();
485
- }
486
-
487
- /**
488
- * Extract relationships for a resource update.
489
- *
490
- * @param Model|object $model
491
- * @param array $new
492
- * @return array
493
- */
494
- private function extractRelationships (object $ model , array $ new ): array
495
- {
496
- return collect ($ this ->existingRelationships ($ model ))
497
- ->map (fn ($ value ) => $ this ->convertExistingRelationships ($ value ))
498
- ->merge ($ new )
499
- ->all ();
436
+ return IncludePaths::fromArray ($ paths );
500
437
}
501
438
502
439
/**
@@ -514,64 +451,6 @@ private function relationshipRules(): array
514
451
->all ();
515
452
}
516
453
517
- /**
518
- * Convert relationships returned by the `existingRelationships()` method.
519
- *
520
- * We support the method returning JSON API formatted relationships, e.g.:
521
- *
522
- * ```
523
- * return [
524
- * 'author' => [
525
- * 'data' => [
526
- * 'type' => 'users',
527
- * 'id' => (string) $record->author->getRouteKey(),
528
- * ],
529
- * ],
530
- * ];
531
- * ```
532
- *
533
- * Or this shorthand:
534
- *
535
- * ```php
536
- * return [
537
- * 'author' => $record->author,
538
- * ];
539
- * ```
540
- *
541
- * This method converts the shorthand into the JSON API formatted relationships.
542
- *
543
- * @param $value
544
- * @return array
545
- */
546
- private function convertExistingRelationships ($ value )
547
- {
548
- if (is_array ($ value ) && array_key_exists ('data ' , $ value )) {
549
- return $ value ;
550
- }
551
-
552
- if (is_null ($ value )) {
553
- return ['data ' => null ];
554
- }
555
-
556
- $ value = $ this ->resources ()->resolve ($ value );
557
-
558
- if ($ value instanceof JsonApiResource) {
559
- return [
560
- 'data ' => [
561
- 'type ' => $ value ->type (),
562
- 'id ' => $ value ->id (),
563
- ],
564
- ];
565
- }
566
-
567
- return [
568
- 'data ' => collect ($ value )->map (fn (JsonApiResource $ resource ) => [
569
- 'type ' => $ resource ->type (),
570
- 'id ' => $ resource ->id ()
571
- ])->all (),
572
- ];
573
- }
574
-
575
454
/**
576
455
* Validate the JSON API document for a resource request.
577
456
*
@@ -618,12 +497,4 @@ private function validateRelationshipDocument(): void
618
497
}
619
498
}
620
499
621
- /**
622
- * @return ResourceContainer
623
- */
624
- final protected function resources (): ResourceContainer
625
- {
626
- return $ this ->jsonApi ()->server ()->resources ();
627
- }
628
-
629
500
}
0 commit comments