Search code examples
phplaravellaravel-7hashids

Modify Laravel model response to use different ID


I'm looking to sort of "intercept" and change a field in a model before it's send back to the client. I have an API with endpoints similar to the following:

Route::get('users/{user}', 'Api\UserController@user');

My application uses vinkla/laravel-hashids, so as far as the client application is concerned, the ID to query for should be something like K6LJwKJ1M8, and not 1. Currently I can query for a user providing the hash-id, but the response comes back with the numeric/auto-incrementing ID.

e.g. For a query such as /api/users/K6LJwKJ1M8 I receive the following response:

{
    "id": 1,
    "address": null,
    "telephone": null,
    "name": "TestNameHere",
    "description": null,
    ...
}

Is there a nice way in Laravel that I could modify all user objects being returned in responses to use the vinkla/laravel-hashids ID, instead of the auto-incrementing ID?

Ideally, the above response would become:

{
    "id": K6LJwKJ1M8,
    "address": null,
    "telephone": null,
    "name": "TestNameHere",
    "description": null,
    ...
}

I was thinking something like using getRouteKey would work, but it doesn't change the object that's sent out in the JSON response.

e.g.

public function getRouteKey() {
    return Hashids::encode($this->attributes['id']);
}

It'd be nice if there was one place to change this since my application has around 40 different routes that I'd otherwise need to change "manually" (e.g. before sending the response do something like $user->id = Hashids::encode($id))


Solution

  • You can use API Resources https://laravel.com/docs/7.x/eloquent-resources#introduction

    API Resource acts as a transformation layer that sits between your Eloquent models and the JSON responses that are actually returned to your application's users.

    You may create an API resource for the user and use it wherever you're returning the user in the response.

    Api resources gives you a lot more control, you could manipulate whatever field you want, send some extra fields using the combination of a few fields, change the name of the fields that you want in your response (xyz => $this->name)

    UserResource

    <?php
    
    namespace App\Http\Resources;
    
    use Illuminate\Http\Resources\Json\JsonResource;
    
    class UserResource extends JsonResource
    {
        public function toArray($request)
        {
            //You can access model properties directly using $this
            return [
                'id' => Hashids::encode($this->id),
                "address": $this->address,
                "telephone": $this->telephone,
                "name": $this->name,
                "description": $this->description,
                 ...
            ];
        }
    }
    

    And then wherever you want to return a user instance.

    Controller

    // $user is a User Model Instance.
    
    return new UserResource($user); 
    

    In case you have a collection

    // $users is a collection of User Model instances.
    
    return UserResource::collection($users);
    

    UserResource will be applied for every model instance in your collection and then returned to you as your JSON response.