4

I have an API that uses API resource and resource collections to correctly format the JSON responses. In order to decouple my controller from my model I use an adapter to query the underlying model. I'd like to pass the adapter return values as arrays, rather than Eloquent models, to ensure that any furture adapters are easier to right in respect to their return data structures. To create the array return values I serialise my adapter Eloquent results with ->toArray().

I have 2 API Resources to correctly format these results, for a single resource I have:

use Illuminate\Http\Resources\Json\Resource;

class Todo extends Resource
{
    /**
     * Transform the resource into an array.
     *
     * @param  \Illuminate\Http\Request  $request
     * @return array
     */
    public function toArray($request)
    {
        return $this->resource;
    }
}

For a resource collection I have:

use Illuminate\Http\Resources\Json\ResourceCollection;

class TodoCollection extends ResourceCollection
{
    /**
     * Transform the resource collection into an array.
     *
     * @param  \Illuminate\Http\Request  $request
     * @return array
     */
    public function toArray($request)
    {
        return [
            'data' => $this->collection
                          ->map
                          ->toArray($request)
                          ->all()
        ];
    }
}

When I return a single resource from my controller with :

use App\Http\Resources\Todo;

public function show($id)
{
    return new Todo($this->todoAdapter->findById($id));
}

and the adapter query as:

public function findById(int $id){
        return TodoModel::findOrFail($id)
                ->toArray();
}

This works as expected. The problem comes when I try to pass an array of a collection of models i.e.

public function index(Request $request)
{
    $todos = $this->todoAdapter->getAllForUserId(Auth::id(), 'created_by', 'desc', self::DEFAULT_PAGINATE);
    return new TodoCollection($todos);
}

and the adapter query as:

public function getAllForUserId(int $userId, string $sortField, string $sortDir, int $pageSize = self::DEFAULT_PAGINATE)
{
        return Todo::BelongsUser($userId)
                                    ->orderBy($sortField, $sortDir)
                                    ->paginate($pageSize)
                                    ->toArray();
}

I get the following error:

"message": "Call to a member function first() on array",
    "exception": "Symfony\\Component\\Debug\\Exception\\FatalThrowableError",
    "file": "/home/vagrant/code/public/umotif/vendor/laravel/framework/src/Illuminate/Http/Resources/CollectsResources.php",
    "line": 24,

I'm guessing that I can't do 'new TodoCollection($todos)' where $todos is an array of results. How would I get my todoCollection to work with arrays? Any suggestions would be much appreciated!

8
  • you could have make this much simplier than this Commented Feb 4, 2018 at 13:13
  • In what way do you mean? Commented Feb 4, 2018 at 13:14
  • you could have passed collection object in your resource and make it an array there as laravel documentation says Commented Feb 4, 2018 at 13:15
  • How would I do that in the resource? Isn't the point of returning an array from the adapter to make it easier to implement other adapters? For example if I'm getting data via an API adapter then I won't need to convert that to a collection if the resource expects an array? Commented Feb 4, 2018 at 13:18
  • please have a look laravel.com/docs/5.5/eloquent-resources#introduction, it says ` Laravel's resource classes allow you to expressively and easily transform your models and model collections into JSON.` Commented Feb 4, 2018 at 13:21

2 Answers 2

0

Your collections toArray is trying to do too much:

$this->collection
         ->map
         ->toArray($request)
         ->all()

Just directly call $this->collection->toArray().

Sign up to request clarification or add additional context in comments.

1 Comment

Tried to replace collections toArray() with return $this->collection->toArray(); but get the same error - thanks for your suggestion though.
0

Just to update this. In the end I found that creating a collection from the array of results and passing that to the resource collection constructor worked, though I did have to add explicit mappings within the resource collection for links and meta etc.

2 Comments

Hi Can you tell me how to retrieve multi array items in the response object in TodoResource file. my $this->response is like array(25) { [0]=> array(9) { ["ID"]=> int(3160) ["UserID"]=> int(3302) ["ItemName"]=> string(1) "2" ["Description"]=> string(1) "2" } }
In the Resource I just added the following to the toArray() method - return $this->resource; If your working with a collection (i.e. ResourceCollection) then convert to an array first ($collectionArray = $this->collection->toArray();), then get the bits you need e.g. return [ 'data' => $this->collection->get('data')];

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.