A Laravel API Resource can be either a single resource or a collection. In some cases, additional parameters are required to be passed to the resource/collection from the controller. Below is a simple example demonstrating the issue using User
as a single/collection resource, and a custom $apple
parameter to be passed to the resource for output. The issue can be seen in the final Output (Collection)
below, where for the fruit
value, we get an incorrect value of banana
for the first user, instead of the correct apple
value (which all other users get). It works perfectly for the single output, just not the collection. See below:
Controller with UserResource (Single)
$user = User::first();
return new UserResource($user, $apple = true); // $apple param passed
Controller with UserResource (Collection)
$users = User::limit(3)->get();
return UserResource::collection($users, $apple = true); // $apple param passed
UserResource
<?php
namespace App\Http\Resources;
use Illuminate\Http\Resources\Json\JsonResource;
class UserResource extends JsonResource {
private $apple;
public function __construct($resource, $apple = false) {
// Ensure we call the parent constructor
parent::__construct($resource);
$this->resource = $resource;
$this->apple = $apple; // $apple param passed
}
public function toArray($request) {
return [
'id' => (int) $this->id,
'name' => $this->name,
'fruit' => $this->apple ? 'apple' : 'banana',
];
}
}
Output (Single)
{
"data": {
"id": 1,
"name": "Peter",
"fruit": "apple" // correct param!
}
}
Output (Collection)
{
"data": [
{
"id": 1,
"name": "Peter",
"fruit": "banana" // INCORRECT param!
},
{
"id": 2,
"name": "Lois",
"fruit": "apple" // correct param!
},
{
"id": 3,
"name": "Brian",
"fruit": "apple" // correct param!
}
]
}
Please note that this is just an example, it can be any amount of random parameters (unrelated to the User
collection, but must be passed for output logic), such as a single value read_at
timestamp from a different table I want to pass once, and do some logic on it in the resource collection before output (like comparison to a user timestamp), or other parameters passed for additional logic if/else
to be performed in the resource file in general to manipulate output of collection. How can this be done?
The following approach worked for me:
UserResource
class UserResource extends Resource{
protected $foo;
public function foo($value){
$this->foo = $value;
return $this;
}
public function toArray($request){
return [
'id' => $this->id,
'name' => $this->name,
'foo' => $this->foo,
];
}
public static function collection($resource){
return new UserResourceCollection($resource);
}
}
UserCollection
class UserResourceCollection extends ResourceCollection{
protected $foo;
public function foo($value){
$this->foo = $value;
return $this;
}
public function toArray($request){
return $this->collection->map(function(UserResource $resource) use($request){
return $resource->foo($this->foo)->toArray($request);
})->all();
// or use HigherOrderCollectionProxy
// return $this->collection->each->foo($this->foo)->map->toArray($request)->all()
// or simple
// $this->collection->each->foo($this->foo);
// return parent::toArray($request);
}
}
Different ways to pass the additional parameter
(new UserResource($user))->foo('bar');
(new UserResourceCollection($user))->foo('bar');
UserResource::make($user)->foo('bar');
UserResourceCollection::make($users)->foo('bar');
UserResource::collection($users)->foo('bar');