Search code examples
jsonlaravellaravel-resource

Laravel format resource collection response error Property [product_id] does not exist on this collection instance


In laravel we can format our json response from resource class as seen below

class ProductsResource extends JsonResource
{
    public function toArray($request)
        {
            return [
                'id'=> $this->product_id ,
                'code'=> $this->product_code,
                'shortdescription'=> $this->product_short_description,
                'image'=> $this->product_image,
                
            ];
        }
}

But when returning resources collection i can't format my collection error Property [product_id] does not exist on this collection instance

class ProductsResource extends ResourceCollection
{
    public function toArray($request)
        {
            return [
                'id'=> $this->product_id ,
                'code'=> $this->product_code,
                'shortdescription'=> $this->product_short_description,
                'image'=> $this->product_image,
                
            ];
        }
}

thanks.


Solution

  • It's because ResourceCollection expects a collection of items instead of a single item. The collection resource expects you to iterate through the collection and cannot perform single enitity routines directly from $this (as it's a collection).

    See resource collections in documentation

    What you are probably looking for is to cast a custom mutation which examples can be found here:

    See custom casts in documentation

    Look/search for Value Object Casting. It is explained thoroughly how to mutate attributes on get and set, which is probably better than a resource collection (if this is the only thing you wish to do with it). This will modify the collection immediately and saves you from having to manually instantiate the resource collection every time you need it (as you are modifying at model level).

    Coming from the docs:

    Value Object Casting You are not limited to casting values to primitive types. You may also cast values to objects. Defining custom casts that cast values to objects is very similar to casting to primitive types; however, the set method should return an array of key / value pairs that will be used to set raw, storable values on the model.

    But to get back on topic...

    If you dump and die: dd($this); you will see there is an attribute called +collection

    In case you wish to transform keys or values, you have to iterate through $this->collection in order to transform the collection values or keys to your requirements.

    As you can see in the parent class Illuminate\Http\Resources\Json\ResourceCollection the method toArray() is already a mapped collection.

    In which you can see it's pointing to $this->collection

    /**
     * Transform the resource into a JSON array.
     *
     * @param  \Illuminate\Http\Request  $request
     * @return array
     */
    public function toArray($request)
    {
        return $this->collection->map->toArray($request)->all();
    }
    

    You could use something like the following. And update the item/key values within this collection map.

    return $this->collection->map(function($item, $key){})->toArray();
    

    if you wish to transform the values before returning it to an array.

    Or a simple foreach like this (have not tested it and there are far better ways of doing this) But for the sake of sharing a simple-to-grasp example:

    $result = [];
    
    // Map the associations to be modified
    $resultMap = [
        'product_id'                 => 'id',
        'product_code'               => 'code',
        'product_short_description'  => 'shortdescription',
        'product_image'              => 'image'
    ];
    
    // Iterate through the collection
    foreach ($this->collection as $index => $item)
        foreach ($item as $key => $value)
            $result[$index][$resultMap[$key]] = $value;
    
    return $result;