Serializing Eloquent Resources to an Array

Published by Dev Kordeš on 08/17/2019

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.

This website uses  Google Analytics  cookies. Beware.