Skip to content

[13.x] Fix JsonApiResource relationships resolved through a closure#60752

Merged
taylorotwell merged 2 commits into
laravel:13.xfrom
markwalet:fix/json-api-closure-relationships
Jul 14, 2026
Merged

[13.x] Fix JsonApiResource relationships resolved through a closure#60752
taylorotwell merged 2 commits into
laravel:13.xfrom
markwalet:fix/json-api-closure-relationships

Conversation

@markwalet

@markwalet markwalet commented Jul 13, 2026

Copy link
Copy Markdown
Contributor

PR description is generated by Claude and checked by myself, just wanted to be open about that.

This PR fixes a crash when a JSON:API relationship is resolved through a closure that returns a resource:

public function toRelationships(Request $request): array
{
    return [
        'author' => UserResource::class,
        'comments' => fn () => CommentResource::collection(
            $request->user()->is($this->resource)
                ? $this->comments
                : $this->comments->where('is_public', true),
        ),
    ];
}

This throws the following error:

Illuminate\Http\Resources\JsonApi\RelationResolver::handle(): Return value
must be of type Illuminate\Database\Eloquent\Collection|Illuminate\Database\Eloquent\Model|null,
Illuminate\Http\Resources\JsonApi\AnonymousResourceCollection returned

RelationResolver::handle() only accepted a raw Collection, Model, or null, so a closure returning CommentResource::collection(...) (an AnonymousResourceCollection) blew up on the return type before the value was ever used.

The fix stays inside RelationResolver. A returned resource collection already carries everything we need — the underlying models on its collection, and the resource class on its collects property — so we unwrap it back into the models and adopt that resource class. A single returned resource is unwrapped the same way. This mirrors what the resolver already does internally when you hand it a class-string; the closure form was simply arriving "pre-wrapped" and the resolver didn't know how to open it.

Closures that already return raw models or Eloquent collections keep working unchanged, so this is fully backward compatible — it only adds handling for return types that previously errored.

Tests are included covering a closure that returns a resource collection, a single resource, raw models, and null.

Closes #60121

@markwalet markwalet force-pushed the fix/json-api-closure-relationships branch from 2b87c05 to 93889c9 Compare July 13, 2026 09:22
@taylorotwell taylorotwell merged commit da16e9a into laravel:13.x Jul 14, 2026
53 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

JsonApiResource not able to lazy-load relations

2 participants