Serializing Eloquent Resources to an Array
Laravel resources are a great way to transform your database models into an API response. But they can be tricky when all you need is for the resource to return a recursively serialized array which wouldn't be returned directly in a http response
Problem
Let's say we have a simple Eloquent resource like this:
<?php
namespace App\Http\Resources;
use Illuminate\Http\Resources\Json\JsonResource;
class PostResource extends JsonResource
{
/**
* Transform the resource into an array.
*
* @param \Illuminate\Http\Request $request
* @return array
*/
public function toArray($request)
{
return [
'id' => $this->id,
'title' => $this->title,
'content' => $this->content,
'tags' => TagResource::collection($this->whenLoaded('tags')),
];
}
}
Now let's see what happens if we call ->toArray()
on it and dd()
the results:
Route::get('/', function () {
$resource = new PostResource(Post::with('tags')->first());
dd($resource->toArray(1));
});
We get this:
array:7 [▼
"id" => 1
"title" => "Title"
"content" => "Content"
"published_at" => "2019-03-01T17:00:00Z"
"tags" => AnonymousResourceCollection {#238 ▶}
]
As we can see, there are 2 problems here
1.) the subresource "tags" isn't actually serialized, it's converted into an object
2.) the array is not wrapped in data
as we'd expect it to
Solution
It's hacky, but it works. We have to call toResponse(1)
with an arbitrary argument ($request
is expected, but anything works), then getContent()
on the response object - which converts it to a JSON string. And then json_decode
that:
dd(json_decode($resource->toResponse(1)->getContent(), true));
Results in:
array:1 [▼
"data" => array:7 [▼
"id" => 1
"title" => "Clean and DRY Polymorphic Relationships"
"content" => "Content"
"published_at" => "2019-03-01T17:00:00Z"
"tags" => array:1 [▼
0 => array:2 [▼
"id" => 1
"name" => "Tag1"
]
]
]
]
Example usage
I ran into this problem when passing the Eloquent Resource into the toArray()
method in the Notification class. The children didn't get serialized and some notification channels didn't handle that well.
I hope we get a nicer method at some point, but for now this is my solution.