t0berius's avatar
Level 13

customize "data" attribute

Dear laravel artisans;

is it possible to customize the data attribute laravel is wrapping around my resource?

OrderCollection

class OrderCollection extends ResourceCollection
{

	public function toArray(Request $request): array
 	{
    	return [
        	'a_custom_name' => $this->collection,
    	];
	}

	//remove some links from pagination
	public function paginationInformation($request, $paginated, $default)
	{
	    //remove links attribute
	    unset($default['meta']['links']);

	    //additional attribute
	    $default['meta']['server'] = "someServer",

	    return $default;
	}

}

OrderResource

class OrderResource extends JsonResource
{
	public function toArray(Request $request): array
	{
	    return [
	        'id' => $this->uuid,
	        'attributes' => [
	            'status_id' => $this->status_id->name,
	            'updatedAt' => $this->updated_at
	        ],
	        'includes' => new MessageResourceCollection($this->whenLoaded('messages')),
	    ];
	}
}

Using it inside a controller:

return new OrderCollection(Order::where('x', '=', 'x')->paginate());

I think because of paginate() all my tries had no success at all. Is there a way how I can customize the data attribute?

Output when commented out toArray inside OrderCollection:

{
	"data": [
	    { 
	    }
	],
	"links": {
	    
	},
	"meta": {
	}
}

Output as soon as I include a_custom_name in OrderCollection:

{
	"data": {
	    "a_custom_name": [
	        {
	        	//data of a single attribute
	        }
	    ]
	},
	"links": {
	    
	},
	"meta": {
	   
	}
}

Expected output

{
	"a_custom_name": [
	    { 
			//data of a single attribute
	    }
	],
	"links": {
	    
	},
	"meta": {
	}
}
0 likes
5 replies
LaryAI's avatar
Level 58

To achieve the expected output where the data attribute is replaced with a custom name like a_custom_name, you need to override the toArray method in your OrderCollection class. However, you also need to ensure that the structure of the response is modified correctly to remove the data wrapper.

Here's how you can do it:

  1. Override the toArray method in the OrderCollection class to return the custom structure.
  2. Ensure that the pagination information is also correctly formatted.

Here's the updated code for your OrderCollection class:

use Illuminate\Http\Resources\Json\ResourceCollection;

class OrderCollection extends ResourceCollection
{
    public function toArray($request): array
    {
        return [
            'a_custom_name' => $this->collection->map(function ($item) use ($request) {
                return (new OrderResource($item))->toArray($request);
            }),
        ];
    }

    // Remove some links from pagination
    public function paginationInformation($request, $paginated, $default)
    {
        // Remove links attribute
        unset($default['meta']['links']);

        // Additional attribute
        $default['meta']['server'] = "someServer";

        return $default;
    }
}

In this code:

  • The toArray method is overridden to return an array with the key a_custom_name containing the collection of OrderResource items.
  • The paginationInformation method is used to customize the pagination metadata, removing the links attribute and adding a custom server attribute.

Your OrderResource class remains the same:

use Illuminate\Http\Resources\Json\JsonResource;

class OrderResource extends JsonResource
{
    public function toArray($request): array
    {
        return [
            'id' => $this->uuid,
            'attributes' => [
                'status_id' => $this->status_id->name,
                'updatedAt' => $this->updated_at,
            ],
            'includes' => new MessageResourceCollection($this->whenLoaded('messages')),
        ];
    }
}

Finally, in your controller, you can return the OrderCollection as usual:

use App\Http\Resources\OrderCollection;
use App\Models\Order;

public function index()
{
    return new OrderCollection(Order::where('x', '=', 'x')->paginate());
}

With these changes, the output should match your expected structure:

{
    "a_custom_name": [
        {
            // data of a single attribute
        }
    ],
    "links": {
        // pagination links
    },
    "meta": {
        // pagination meta
        "server": "someServer"
    }
}

This approach ensures that the data attribute is replaced with your custom name while maintaining the correct structure for the pagination information.

t0berius's avatar
Level 13

@laryai solution is throwing

"message": "Call to a member function isNotEmpty() on null",
"exception": "Error",

I think it's somehow caused by

'includes' => new MessageResourceCollection($this->whenLoaded('messages')),

in toArray of OrderResource. When I comment this line out I get the following structure returned:

{
	"data": {
	    "a_custom_name": [
	        {
	            // data of a single attribute
	        }
	    ]
	},
	"links": {
		// pagination links
	},
	"meta": {
	 	// pagination meta
	    "server": "someServer"
	}
}

So not fine at all :/.

jlrdw's avatar

I suggest letting the database do the work then just paginate your query results.

Please or to participate in this conversation.