Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

christiangerdes's avatar

Data model property in API Resource, how?

Hi,

I have a model with a data field and I can't make it work with Laravel's API Resources. I suspect it has something to do with data being a reserved keywork?

Migration:

Schema::create('stores', function (Blueprint $table) {
            $table->increments('id');
            $table->string('title');
            $table->json('data');
            $table->softDeletes();
            $table->timestamps();
});

API Resource

class StoreResource extends Resource
{
    /**
     * Transform the resource into an array.
     *
     * @param  \Illuminate\Http\Request
     * @return array
     */
    public function toArray($request)
    {
        return [
            'id' => $this->id,
            'title' => $this->title,
            'data' => $this->data
        ];
    }
}

Controller

public function show(Store $store)
    {
        return new StoreResource($store);
    }

Response - Please note the response is not wrapped in data like the next response where the data key has been renamed to something else

{
    "id": 1,
    "title": "Vikleuniverset.dk",
    "data": {
        "key": "value",
        "john": "doe"
    }
}

Response with renamed data key

{
    "data": {
        "id": 1,
        "title": "Vikleuniverset.dk",
        "another": {
            "key": "value",
            "john": "doe"
        }
    }
}

How do I fix this to always wrap the response in a data property?

0 likes
7 replies
JohnBraun's avatar

My guess is that this indeed has to do with the reserved word data, as Laravel tries to be smart about this and prevent double wrapping (https://laravel.com/docs/5.8/eloquent-resources#data-wrapping). To circumvent this, you could simply wrap everything in a 'data' key yourself:

class StoreResource extends Resource
{
    /**
     * Transform the resource into an array.
     *
     * @param  \Illuminate\Http\Request
     * @return array
     */
    public function toArray($request)
    {
        return [
                'data' => [
                        'id' => $this->id,
                        'title' => $this->title,
                        'data' => $this->data
                ]
        ];
    }
}
christiangerdes's avatar

@JOHNBRAUN - Doing that will result in double wrapping when using StoreResource::collection. That will not work

1 like
JohnBraun's avatar

Allright, I've been playing around a bit and think you might need to create a separate resource for your collections, according to Jeffrey in his video on API resources.

I created a StoreCollection:

<?php

namespace App\Http\Resources;

use Illuminate\Http\Resources\Json\ResourceCollection;

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

Then, I defined in my routes file:

<?php

use App\Http\Resources\StoreCollection;

Route::get('/stores', function () {
    $stores = App\Stores::get();

    return new StoreCollection($stores);
});

And got the following response:

{
    "data": [
        {
            "id": 1,
            "title": "Test",
            "data": "{}",
            "created_at": "2019-06-19 15:30:07",
            "updated_at": "2019-06-19 15:30:07"
        },
        {
            "id": 2,
            "title": "Test 2",
            "data": "{}",
            "created_at": "2019-06-19 20:09:41",
            "updated_at": "2019-06-19 20:09:41"
        }
    ]
}
shez1983's avatar

instead of doing all this why not just name the field from data to something else? or change the name of the wrapper from data to something else..

christiangerdes's avatar

@SHEZ1983 - Well, because naming is important and data is the best name for this field. Secondly, it’s important to understand why something isn’t working instead of just doing something until it works. Makes a must stronger code base and understanding.

1 like
christiangerdes's avatar
Level 16

I found this is the laravel code base:

/**
  * Determine if we have a default wrapper and the given data is unwrapped.
  *
  * @param  array  $data
  * @return bool
*/
protected function haveDefaultWrapperAndDataIsUnwrapped($data)
{
    return $this->wrapper() && ! array_key_exists($this->wrapper(), $data);
}

It turns out the wrapper is reserved and cannot be used for resource field keys without disabling wrapping for single models. It works only when using collections.

Please or to participate in this conversation.