Recently, I applied for a software engineering job, and at first, I was too excited about it. Yes, there were some tasks to do that finally made me give up because I didn't have much time to do all of what was wanted in the description, and I also had another interviews. So I didn't get through the tasks. Believe me or not, it was like an actual backlog!
The problem
But, one of the tasks impressed me to do some stuff and write this post. I tell you about the question and clarify the problem:
- We build an API (v1).
- Then we have another version (v2) with some new features.
- Not every feature has changed, let's keep it DRY!
Controllers/
├─ v1/
│ ├─ PostController.php
├─ v2/
│ ├─ PostController.php
Let's suppose that we don't have the show()
method in the v2/PostController.php
because we don't need it. But the first version's index()
method has a totally different implementation than the second version. Consider that the boss doesn't like any duplication!
How do you implement it?
Solution #1: Inheritance
I assume that you know what inheritance is and get directly into my first solution to the problem.
In the first version, we implement everything we need normally. When we start building the second version's endpoints, we just implement what's changed. I demonstrate the idea with this example:
api.php
Route::get('/v1/posts', [V1PostController::class, 'index']);
Route::get('/v1/posts/{id}', [V1PostController::class, 'show']);
Route::get('/v2/posts', [V2PostController::class, 'show']);
Route::get('/v2/posts/{id}', [V2PostController::class, 'show']);
V1/PostController.php
<?php
namespace App\Http\Controllers\API\V1;
use App\Http\Controllers\Controller;
class PostController extends Controller
{
public function index()
{
return 'V1 index';
}
public function show($id)
{
return "V1 show: {$id}";
}
}
V2/PostController.php
<?php
namespace App\Http\Controllers\API\V2;
use App\Http\Controllers\API\V1\PostController as V1PostController;
class PostController extends V1PostController
{
public function index()
{
return 'V2 index';
}
}
As we haven't overridden the show()
method, the second version's controller provides the first version's functionality and opening https://localhost/api/v2/posts/1
shows us:
V1 show: 1
Solution #2: handleVersion()
The problem with the previous solution is that we need to define all the routes, even if they do the same thing in the previous version. What's the point of defining the /v2/posts/{id}
route if it's not actually implemented? Are we supposed to repeat a hundred routes per version? Of course not!
Another fix came into my head! How does router work? Let's take a glance:
Route::get('uri here', ['controller namespace', 'method name']);
So I wrote such a function:
helpers.php
function handleVersion(string $controller, string $method)
{
$latestApiVersion = config('app.latest_api_version');
preg_match("/api\/v(\\d+)/i", request()->path(), $apiVersion);
$apiVersion = $apiVersion[1] ?? $latestApiVersion;
if ($apiVersion > $latestApiVersion) {
abort(404);
}
$namespace = "App\Http\Controllers\API\V{$apiVersion}\\{$controller}";
while (! method_exists(app($namespace), $method)) {
if ($apiVersion === 0) {
abort(404);
}
$apiVersion--;
$namespace = "App\Http\Controllers\API\V{$apiVersion}\\{$controller}";
}
return [$namespace, $method];
}
And used it as bellow:
api.php
Route::get('{version}/posts', handleVersion('PostController', 'index'));
Route::get('{version}/posts/{id}', handleVersion('PostController', 'show'));
Now, I don't have to define plenty of pointless routes anymore!
Solution #3: A combination of my two solutions
I liked the idea of extending the previous version controllers; hence, I decided to combine the two solutions! If you're going to create empty controllers, you can get rid of the while()
loop in the handleVersion()
.
Last words!
I truly enjoyed facing this challenge, and this post is about two ideas I'd thought about. If you have a better idea, please feel free to share it with us in the comments.
Top comments (4)
Thanks a million for sharing your insights on coding! 🚀 Your post not only inspired me but also added valuable knowledge to the community. Grateful to have friends like you who contribute to our learning journey. Keep rocking! 💻🙌
Thank you for reading!
thank you so much for sharing your ideas , i personally enjoyed it a lot !! 🙌
Thanks dear Saleh!