Courses

Laravel API Code Review and Refactor

Single Responsibility: Move Controller Code to Trait

You're reading a FREE PREVIEW of a PREMIUM course.

Link to the repository

[Only for premium members]

In this lesson, we will discuss SOLID code with the Single Responsibility Principle and clearer method names.


The Problem: Base Controller and Trait

First, the project has a "Base" API Controller with a few methods that can be reused in other Controllers. Some of them are for typical API responses.

app/Http/Controllers/Api/V1/ApiController.php

class ApiController extends Controller
 
// ...
 
public function notAuthorized(string $message = 'You are not authorized.')
{
return $this->error($message, Response::HTTP_UNAUTHORIZED);
}
 
public function notFound(string $message = 'Not Found.')
{
return $this->error($message, Response::HTTP_NOT_FOUND);
}
 
public function unexpectedError(string $message = 'An unexpected error occurred.')
{
return $this->error($message, Response::HTTP_INTERNAL_SERVER_ERROR);
}
 
public function dbError(string $message = 'Database error.')
{
return $this->error($message, Response::HTTP_INTERNAL_SERVER_ERROR);
}

But wait, what is that $this->error()?

Then I noticed a trait for API Responses:

app/Http/Controllers/Api/V1/ApiController.php

use App\Traits\V1\ApiResponses;
 
class ApiController extends Controller
{
use ApiResponses;

Inside that Trait, we have more "general" methods for API responses:

app/Traits/V1/ApiResponses.php:

trait ApiResponses
{
protected function ok(string $message, array $data = [])
{
return $this->success($message, $data, 200);
}
 
protected function success(string $message, array $data = [], int $statusCode = 200)
{
return response()->json([
'data' => $data,
'message' => $message,
'status' => $statusCode,
], $statusCode);
}
 
protected function error($message, $statusCode)
{
return response()->json(['errors' => $message, 'status' => $statusCode], $statusCode);
}
}

And I had a few questions here:

  1. Traits are usually used when you would use them multiple times in different places. I am unsure if another BaseController is planned: maybe in V2 much later? So why trait?
  2. Okay, if you want to use this Trait, then why mix methods in the Controller and in that Trait? According to SRP, they must be in one place.

So, I decided to...

The full lesson is only for Premium Members.
Want to access all 15 lessons of this course? (56 min read)

You also get:

  • 76 courses
  • Premium tutorials
  • Access to repositories
  • Private Discord