I'm about to ...
And I'm thinking ... really? They haven't built this in... what am I missing here? There's gotta be a quick, turn-key for this. I'm interested in knowing how other people are doing this, so please chime in.
Have a look at resources: https://laravel.com/docs/5.7/eloquent-resources
And add your logic to resources so that you display different versions of a model depending on the API version. You can still make use of $appends
and $hidden
.
With this approach we return a Resource of a model rather than the model itself.
Here is an example of a UserResource for different API versions:
class UserResource extends JsonResource
{
private $apiVersion;
public function __construct($resource, int $apiVersion = 2) {
$this->apiVersion = $apiVersion; // OPTION 1: Pass API version in the constructor
parent::__construct($resource);
}
public function toArray($request): array
{
// OPTION 2: Get API version in the request (ideally header)
// $apiVersion = $request->header('x-api-version', 2);
/** @var User $user */
$user = $this->resource;
return [
'type' => 'user',
'id' => $user->id,
$this->mergeWhen($this->apiVersion < 2, [
'name' => "{$user->first_name} {$user->last_name}",
], [
'name' => [
'first' => $user->first_name,
'last' => $user->last_name
],
]),
'score' => $user->score,
];
}
}
The you can call:
$user = User::find(5);
return new UserResource($user);
If you need a different connection you can do:
$user = User::on('second_db_connection')->find(5);
So V1 API gets:
{
id: 5,
name: "John Smith",
score: 5
}
and V2 API gets:
{
id: 5,
name: {
first: "John",
last: "Smith",
},
score: 5
}
Now if later you wanted to rename score to points in your DB, and in V3 of your API you also wanted to change your JSON output, but maintain backwards compatibility you can do:
$this->mergeWhen($this->apiVersion < 3, [
'score' => $user->points,
], [
'points' => $user->points,
])
You can easily prefix routes as mentioned here: https://laravel.com/docs/5.7/routing#route-group-prefixes
Route::prefix('v1')->group(function () {
Route::get('users', function () {
// ...
});
});
To do custom route model bindings have a look at: https://laravel.com/docs/5.7/routing#route-model-binding
e.g.
Route::bind('user', function ($value) {
return App\User::where('name', $value)->first() ?? abort(404); // your customer logic
});