Now, I want to show you another practical example of Inheritance and Traits. What if you want to have strict rules for API responses instead of calling return response()->json()
in every Controller method?
For example, see this code:
TaskController:
public function index(){ return response()->json([ 'success' => true, 'data' => Task::paginate() ]);}
As you can see, the API client expects success => true
to be returned and the actual data in a data
key.
But you don't have any guarantees that other developers on your team (including your future self) will remember to use this structure in other Controllers/methods. How do we "enforce" it?
The answer is to create specific methods to be used, like $this->respondSuccess()
, $this->respondError()
, and others. The question is WHERE to create them to be used in all Controllers?
I will show you two ways:
If you generate a Controller with php artisan make:controller
, you should see it extends a "general" Controller:
use App\Http\Controllers\Controller; class TaskController extends Controller{ // ...}
If we look inside that "general" Controller, it looks... empty?
app/Http/Controllers/Controller.php:
namespace App\Http\Controllers; use Illuminate\Foundation\Auth\Access\AuthorizesRequests;use Illuminate\Foundation\Bus\DispatchesJobs;use Illuminate\Foundation\Validation\ValidatesRequests;use Illuminate\Routing\Controller as BaseController; class Controller extends BaseController{ use AuthorizesRequests; use DispatchesJobs; use ValidatesRequests;}
Yup, that's all the code. It just used three Traits from Laravel core but doesn't have any methods or properties itself.
So, why is this Controller not a part of the Laravel core, then?
The thing is that the "general" Controller is not in the /vendor
folder, which means we are allowed to edit it!
So, we can create our own methods like respondSuccess()
here, and then all the Laravel Controllers that extend this Controller will also inherit those methods to be used.
Like this:
app/Http/Controllers/Controller.php:
namespace App\Http\Controllers; use Illuminate\Foundation\Auth\Access\AuthorizesRequests;use Illuminate\Foundation\Bus\DispatchesJobs;use Illuminate\Foundation\Validation\ValidatesRequests;use Illuminate\Routing\Controller as BaseController; class Controller extends BaseController{ use AuthorizesRequests; use DispatchesJobs; use ValidatesRequests; public function respondSuccess($data) { return response()->json([ 'success' => true, 'data' => $data ]); }}
And then in any of our "regular" Controllers:
app/Http/Controllers/TaskController.php:
use App\Http\Controllers\Controller; class TaskController extends Controller{ public function index() { return $this->respondSuccess(Task::paginate()); } // ... other methods}
Another alternative can be used if you want only SOME of your Controllers to have those methods. For example, when you have Web Controllers and API Controllers, it makes sense only for the API Controllers to have the respondSuccess()
method.
Then you put those respondXXXXX()
methods in a Trait and include that Trait only in the API Controllers.
You can create Traits in any folder you want, just don't forget the correct namespace on top. Also, there's no make:trait
Artisan command, so you need to create that file manually.
app/Http/Traits/APIResponsesTrait.php
namespace App\Http\Traits; trait APIResponsesTrait{ public function respondSuccess($data) { return response()->json([ 'success' => true, 'data' => $data ]); }}
And then, in any of your API Controllers, do it like this:
app/Http/Controllers/API/TaskController.php:
namespace App\Http\Controllers\Api; use App\Http\Controllers\Controller;use App\Http\Traits\APIResponsesTrait; class TaskController extends Controller{ use APIResponsesTrait; public function index() { return $this->respondSuccess(Task::paginate()); } // ... other methods}
And if you have a non-API Controller, just don't include that Trait:
app/Http/Controllers/TaskController.php:
namespace App\Http\Controllers; use App\Http\Controllers\Controller; class TaskController extends Controller{ public function index() { // $this->respondSuccess() would throw an error here }}
Both of the options are okay, and it's a personal preference. But with those examples, I wanted to show you how you can practically use the knowledge about OOP in Laravel structure, not just with packages.