I've run into a problem where I've used an attribute casted as an array in a Laravel resource and I wanted to preserve the keys of that array. By default, Laravel will re-index the array, which is not what I wanted.

To preserve the keys, you can use the preserveKeys method on the collection. Here's an example:

namespace App\Http\Resources;

use Illuminate\Http\Resources\Json\JsonResource;

class User extends JsonResource
{
    /**
     * Indicates if the resource's collection keys should be preserved.
     *
     * @var bool
     */
    public $preserveKeys = true;
}

When the preserveKeys property is set to true, collection keys will be preserved:

use App\User;
use App\Http\Resources\User as UserResource;

Route::get('/user', function () {
    return UserResource::collection(User::all()->keyBy->id);
});

What I didn't know is that this didn't only apply to the resource, but also to the attributes of the resource.

If you don't want to enable this option for the complete resource, once approach you can take is to create a new resource like this:

namespace App\Http\Resources;

use Illuminate\Http\Request;
use Illuminate\Http\Resources\Json\JsonResource;

class ArrayPreservingKeysResource extends JsonResource
{
    /**
     * Indicates if the resource's collection keys should be preserved.
     *
     * @var bool
     */
    public $preserveKeys = true;

    public function toArray(Request $request): array
    {
        return $this->resource;
    }
}

You can then apply to any array you want to preserve the keys of in the resource:

namespace App\Http\Resources;

use App\Domains\Predictions\Models\Prediction;
use Illuminate\Http\Request;
use Illuminate\Http\Resources\Json\JsonResource;

/**
 * @mixin Company
 */
class CompanyResource extends JsonResource
{
    public function toArray(Request $request): array
    {
        return [
            'id' => $this->id,
            'name' => $this->name,
            'data' => new ArrayPreservingKeysResource($this->data),
        ];
    }
}