@@ -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
0 commit comments