Skip to content

Commit cbabe7f

Browse files
committed
wip
Signed-off-by: Mior Muhammad Zaki <[email protected]>
1 parent 47efeb8 commit cbabe7f

File tree

3 files changed

+91
-10
lines changed

3 files changed

+91
-10
lines changed

src/Illuminate/Http/Resources/JsonApi/Concerns/ResolvesJsonApiElements.php

Lines changed: 32 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,8 @@ trait ResolvesJsonApiElements
3333
*/
3434
protected bool $includesPreviouslyLoadedRelationships = false;
3535

36+
protected static int $nestedRelationshipsDepth = 3;
37+
3638
/**
3739
* Cached loaded relationships map.
3840
*
@@ -45,6 +47,14 @@ trait ResolvesJsonApiElements
4547
*/
4648
protected array $loadedRelationshipIdentifiers = [];
4749

50+
/**
51+
* Determine maximum relationship nesting.
52+
*/
53+
public static function maxRelationshipNesting(int $depth): void
54+
{
55+
static::$nestedRelationshipsDepth = max(0, $depth);
56+
}
57+
4858
/**
4959
* Resolves `data` for the resource.
5060
*/
@@ -205,6 +215,7 @@ protected function compileResourceRelationships(JsonApiRequest $request): void
205215
$this->resource,
206216
$relationResolver,
207217
$relatedModels,
218+
depth: 1
208219
);
209220
}
210221
}))->all();
@@ -217,7 +228,8 @@ protected function compileResourceRelationshipUsingResolver(
217228
JsonApiRequest $request,
218229
mixed $resource,
219230
RelationResolver $relationResolver,
220-
Collection|Model|null $relatedModels
231+
Collection|Model|null $relatedModels,
232+
int $depth
221233
): Generator {
222234
$relationName = $relationResolver->relationName;
223235
$resourceClass = $relationResolver->resourceClass();
@@ -236,15 +248,15 @@ protected function compileResourceRelationshipUsingResolver(
236248

237249
$isUnique = ! $relationship instanceof BelongsToMany;
238250

239-
yield $relationName => ['data' => $relatedModels->map(function ($relatedModel) use ($request, $resourceClass, $isUnique) {
251+
yield $relationName => ['data' => $relatedModels->map(function ($relatedModel) use ($request, $resourceClass, $isUnique, $depth) {
240252
$relatedResource = rescue(fn () => $relatedModel->toResource($resourceClass), new JsonApiResource($relatedModel));
241253

242254
return transform(
243255
[$relatedResource->resolveResourceType($request), $relatedResource->resolveResourceIdentifier($request)],
244-
function ($uniqueKey) use ($request, $relatedModel, $relatedResource, $isUnique) {
256+
function ($uniqueKey) use ($request, $relatedModel, $relatedResource, $isUnique, $depth) {
245257
$this->loadedRelationshipsMap[] = [$relatedResource, ...$uniqueKey, $isUnique];
246258

247-
$this->compileIncludedNestedRelationshipsMap($request, $relatedModel, $relatedResource);
259+
$this->compileIncludedNestedRelationshipsMap($request, $relatedModel, $relatedResource, depth: $depth);
248260

249261
return [
250262
'id' => $uniqueKey[1],
@@ -275,10 +287,10 @@ function ($uniqueKey) use ($request, $relatedModel, $relatedResource, $isUnique)
275287

276288
yield $relationName => ['data' => transform(
277289
[$relatedResource->resolveResourceType($request), $relatedResource->resolveResourceIdentifier($request)],
278-
function ($uniqueKey) use ($relatedModel, $relatedResource, $request) {
290+
function ($uniqueKey) use ($relatedModel, $relatedResource, $request, $depth) {
279291
$this->loadedRelationshipsMap[] = [$relatedResource, ...$uniqueKey, true];
280292

281-
$this->compileIncludedNestedRelationshipsMap($request, $relatedModel, $relatedResource);
293+
$this->compileIncludedNestedRelationshipsMap($request, $relatedModel, $relatedResource, depth: $depth);
282294

283295
return [
284296
'id' => $uniqueKey[1],
@@ -291,14 +303,24 @@ function ($uniqueKey) use ($relatedModel, $relatedResource, $request) {
291303
/**
292304
* Compile included relationships map.
293305
*/
294-
protected function compileIncludedNestedRelationshipsMap(JsonApiRequest $request, Model $relation, JsonApiResource $resource): void
295-
{
306+
protected function compileIncludedNestedRelationshipsMap(
307+
JsonApiRequest $request,
308+
Model $relation,
309+
JsonApiResource $resource,
310+
int $depth
311+
): void {
312+
if ($depth > static::$nestedRelationshipsDepth) {
313+
return;
314+
}
315+
316+
$depth++;
317+
296318
(new Collection($resource->toRelationships($request)))
297319
->transform(fn ($value, $key) => is_int($key) ? new RelationResolver($value) : new RelationResolver($key, $value))
298320
->mapWithKeys(fn ($relationResolver) => [$relationResolver->relationName => $relationResolver])
299321
->filter(fn ($value, $key) => in_array($key, array_keys($relation->getRelations())))
300-
->each(function ($relationResolver, $key) use ($relation, $request) {
301-
$this->compileResourceRelationshipUsingResolver($request, $relation, $relationResolver, $relation->getRelation($key));
322+
->each(function ($relationResolver, $key) use ($relation, $request, $depth) {
323+
$this->compileResourceRelationshipUsingResolver($request, $relation, $relationResolver, $relation->getRelation($key), depth: $depth);
302324
});
303325
}
304326

src/Illuminate/Http/Resources/JsonApi/JsonApiResource.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -250,5 +250,6 @@ public static function flushState()
250250
parent::flushState();
251251

252252
static::$jsonApiInformation = [];
253+
static::$nestedRelationshipsDepth = 3;
253254
}
254255
}

tests/Integration/Http/Resources/JsonApi/JsonApiResourceTest.php

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
namespace Illuminate\Tests\Integration\Http\Resources\JsonApi;
44

5+
use Illuminate\Http\Resources\JsonApi\JsonApiResource;
56
use Illuminate\Tests\Integration\Http\Resources\JsonApi\Fixtures\Comment;
67
use Illuminate\Tests\Integration\Http\Resources\JsonApi\Fixtures\Post;
78
use Illuminate\Tests\Integration\Http\Resources\JsonApi\Fixtures\Profile;
@@ -465,6 +466,63 @@ public function testItCanResolveRelationshipWithRecursiveNestedRelationship()
465466
->assertJsonMissing(['jsonapi']);
466467
}
467468

469+
public function testItCanResolveRelationshipWithRecursiveNestedRelationshipLimitedToDepthConfiguration()
470+
{
471+
JsonApiResource::maxRelationshipNesting(2);
472+
473+
$now = $this->freezeSecond();
474+
475+
$user = User::factory()->create();
476+
477+
$profile = Profile::factory()->create([
478+
'user_id' => $user->getKey(),
479+
'date_of_birth' => '2011-06-09',
480+
'timezone' => 'America/Chicago',
481+
]);
482+
483+
$this->getJson("/users/{$user->getKey()}?".http_build_query(['include' => 'profile.user.profile']))
484+
->assertHeader('Content-type', 'application/vnd.api+json')
485+
->assertExactJson([
486+
'data' => [
487+
'attributes' => [
488+
'email' => $user->email,
489+
'name' => $user->name,
490+
],
491+
'id' => (string) $user->getKey(),
492+
'type' => 'users',
493+
'relationships' => [
494+
'profile' => [
495+
'data' => ['id' => (string) $profile->getKey(), 'type' => 'profiles'],
496+
],
497+
],
498+
],
499+
'included' => [
500+
[
501+
'attributes' => [
502+
'date_of_birth' => '2011-06-09',
503+
'timezone' => 'America/Chicago',
504+
],
505+
'id' => (string) $profile->getKey(),
506+
'type' => 'profiles',
507+
'relationships' => [
508+
'user' => [
509+
'data' => ['id' => (string) $user->getKey(), 'type' => 'users'],
510+
],
511+
],
512+
],
513+
[
514+
'attributes' => [
515+
'email' => $user->email,
516+
'name' => $user->name,
517+
],
518+
'id' => (string) $user->getKey(),
519+
'type' => 'users',
520+
],
521+
],
522+
])
523+
->assertJsonMissing(['jsonapi']);
524+
}
525+
468526
public function testItCanResolveRelationshipWithoutRedundantIncludedRelationship()
469527
{
470528
$now = $this->freezeSecond();

0 commit comments

Comments
 (0)