I was wondering if it is possible to define different data for item resource and collection resource.
For collection I only want to send ['id', 'title', 'slug']
but the item resource will contain extra details ['id', 'title', 'slug', 'user', etc.]
I want to achieve something like:
class PageResource extends Resource
{
/**
* Transform the resource into an array.
*
* @param \Illuminate\Http\Request
* @return array
*/
public function toArray($request)
{
return [
'id' => $this->id,
'title' => $this->title,
'slug' => $this->slug,
'user' => [
'id' => $this->user->id,
'name' => $this->user->name,
'email' => $this->user->email,
],
];
}
}
class PageResourceCollection extends ResourceCollection
{
/**
* Transform the resource collection into an array.
*
* @param \Illuminate\Http\Request
* @return array
*/
public function toArray($request)
{
return [
'id' => $this->id,
'title' => $this->title,
'slug' => $this->slug,
];
}
}
PageResourceCollection will not work as expected because it uses PageResource so it needs
return [
'data' => $this->collection,
];
I could duplicate the resource into PageFullResource
/ PageListResource
and PageFullResourceCollection
/ PageListResourceCollection
but I am trying to find a better way to achieve the same result.
The Resource class has a collection method on it. You can return that as the parameter input to your ResourceCollection, and then specify your transformations on the collection.
Controller:
class PageController extends Controller
{
public function index()
{
return new PageResourceCollection(PageResource::collection(Page::all()));
}
public function show(Page $page)
{
return new PageResource($page);
}
}
Resources:
class PageResource extends Resource
{
public function toArray($request)
{
return [
'id' => $this->id,
'title' => $this->title,
'slug' => $this->slug,
'user' => [
'id' => $this->user->id,
'name' => $this->user->name,
'email' => $this->user->email,
],
];
}
}
class PageResourceCollection extends ResourceCollection
{
public function toArray($request)
{
return [
'data' => $this->collection->transform(function($page){
return [
'id' => $page->id,
'title' => $page->title,
'slug' => $page->slug,
];
}),
];
}
}
If you want the response fields to have the same value in the Resource and Collection, you can reuse the Resource inside the Collection
PersonResource.php
<?php
namespace App\Http\Resources;
use Illuminate\Http\Resources\Json\Resource;
class PersonResource extends Resource
{
/**
* Transform the resource into an array.
*
* @param \Illuminate\Http\Request $request
* @return array
*/
public function toArray($request)
{
// return parent::toArray($request);
return [
'id' => $this->id,
'person_type' => $this->person_type,
'first_name' => $this->first_name,
'last_name' => $this->last_name,
'created_at' => (string) $this->created_at,
'updated_at' => (string) $this->updated_at,
];
}
}
PersonCollection.php
<?php
namespace App\Http\Resources;
use Illuminate\Http\Resources\Json\ResourceCollection;
class PersonCollection extends ResourceCollection
{
/**
* Transform the resource collection into an array.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\Resources\Json\AnonymousResourceCollection
*/
public function toArray($request)
{
// return parent::toArray($request);
return PersonResource::collection($this->collection);
}
}
The accepted answer works, if you are not interested in using links and meta data. If you want, simply return:
return new PageResourceCollection(Page::paginate(10));
in your controller. You should also look to eager load other dependent relationships before passing over to the resource collection.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With