Customizing Resource Collection Mapping in Laravel

Customizing Resource Collection Mapping in Laravel

Laravel's resource collections provide a powerful way to transform your data for API responses. By default, Laravel makes some assumptions about how your resources should be mapped, but sometimes you need more control. Let's explore how to customize resource collection mapping to fit your specific needs.

Default Behavior

Typically, when you create a resource collection, Laravel automatically maps each item in the collection to its corresponding singular resource. For instance, a UserCollection would normally map to UserResource for each item.

The $collects Property

Laravel provides the $collects property to allow you to specify which resource class should be used for the items in your collection. This is particularly useful when you want to use a different resource class than what Laravel would assume by default.

Here's how you can implement this:

<?php

namespace App\Http\Resources;

use Illuminate\Http\Resources\Json\ResourceCollection;

class UserCollection extends ResourceCollection
{
    /**
     * The resource that this resource collects.
     *
     * @var string
     */
    public $collects = Member::class;

    // ... other methods
}

In this example, instead of using UserResource, the collection will use Member resource for each item.

Real-World Scenario

Let's consider a scenario where you have a Team model with a collection of User models, but you want to represent users as "members" in your API response.

First, create a MemberResource:

<?php

namespace App\Http\Resources;

use Illuminate\Http\Resources\Json\JsonResource;

class MemberResource extends JsonResource
{
    public function toArray($request)
    {
        return [
            'id' => $this->id,
            'name' => $this->name,
            'role' => $this->pivot->role,
            'joined_at' => $this->pivot->created_at->toDateString(),
        ];
    }
}

Now, create a TeamResource that uses MemberResource for its users:

<?php

namespace App\Http\Resources;

use Illuminate\Http\Resources\Json\JsonResource;

class TeamResource extends JsonResource
{
    public function toArray($request)
    {
        return [
            'id' => $this->id,
            'name' => $this->name,
            'members' => MemberResource::collection($this->users),
        ];
    }
}

Finally, if you want a collection of teams, you can create a TeamCollection that ensures MemberResource is used for all nested user data:

<?php

namespace App\Http\Resources;

use Illuminate\Http\Resources\Json\ResourceCollection;

class TeamCollection extends ResourceCollection
{
    public $collects = TeamResource::class;

    public function toArray($request)
    {
        return [
            'data' => $this->collection,
            'total_teams' => $this->collection->count(),
            'total_members' => $this->collection->sum(function ($team) {
                return $team->users->count();
            }),
        ];
    }
}

Now, when you return this collection from a controller:

return new TeamCollection(Team::with('users')->get());

You'll get a response where each team's users are represented as "members" using the MemberResource, providing a consistent and customized API structure.

Conclusion

The $collects property in Laravel resource collections offers a flexible way to customize how your data is transformed for API responses. By specifying which resource class to use for collection items, you can create more expressive and tailored API structures that better fit your application's domain language and requirements.

If this guide was helpful to you, subscribe to my daily newsletter and give me a follow on X/Twitter. It helps a lot!

Subscribe to Harris Raftopoulos

Don’t miss out on the latest issues. Sign up now to get access to the library of members-only issues.
jamie@example.com
Subscribe