In this blog post, I'd like to highlight two tips to make controllers in Laravel feel much lighter.

Controllers don't need to extend anything #

At the time of this writing, this is what you get when you run php artisan make:controller :

namespace App \ Http \ Controllers ; use Illuminate \ Http \ Request ; class MyController extends Controller { }

This controller extends a base controller App\Http\Controllers\Controller . This is the content of that base controller.

namespace App \ Http \ Controllers ; use Illuminate \ Foundation \ Bus \ DispatchesJobs ; use Illuminate \ Routing \ Controller as BaseController ; use Illuminate \ Foundation \ Validation \ ValidatesRequests ; use Illuminate \ Foundation \ Auth \ Access \ AuthorizesRequests ; class Controller extends BaseController { use AuthorizesRequests , DispatchesJobs , ValidatesRequests ; }

That controller extends another one: Illuminate\Routing\Controller . To keep this post short, I'm not going to paste the contents of that one here. Just know that it contains some functions to register middleware and some functions to call other methods on a controller.

There seems to be a widespread belief that, in order for a controller to work, it should extend the Illuminate -controller. But you know what? Controllers don't need to extend anything. Here's an example of a perfectly valid controller.

namespace App \ Http \ Controllers ; class MyController { }

In all our client projects at Spatie the controllers don't extend anything by default. If a controller needs to validate or authorize a request, we use the AuthorizesRequests or ValidatesRequests trait on that controller.

Controllers are better off without a default namespace #

In a vanilla Laravel app, a controller lives in the App\Http\Controllers namespace. This is being set up by default in the RouteServiceProvider by that namespace call.

namespace App \ Providers ; use Illuminate \ Support \ Facades \ Route ; use Illuminate \ Foundation \ Support \ Providers \ RouteServiceProvider as ServiceProvider ; class RouteServiceProvider extends ServiceProvider { protected $namespace = 'App\Http\Controllers' ; protected function mapWebRoutes () { Route::middleware( 'web' ) ->namespace( $this ->namespace) ->group(base_path( 'routes/web.php' )); } }

This enables us to just use a string in the routes file to for example route an URL to a controller like App\Http\Controllers\MyController :

Route::get( 'my-url' , 'MyController@index' );

Sure, that default namespace saves us some keystrokes in the routes file, but that's the only advantage it gives us.

Let's see what advantages we gain by not setting up a default namespace. If you delete that protected $namespace and ->namespace call in the RouteServiceProvider you'll have to specify the fully qualified namespace in the routes file.

use App \ Http \ Controllers \ MyController ; Route::get( 'my-url' , [MyController::class, 'index' ]);

If you only use one method in your controller, you could opt to use __invoke() . By using that method, you don't need that tuple notation anymore in the routes file.

use App \ Http \ Controllers \ MyController ; Route::get( 'my-url' , MyController::class);

Replacing a short string based reference by a the full qualified namespace of a controller has several benefits. If you use an IDE like PhpStorm, it now knows which class is being used. When you start typing a class, it'll try to autocomplete it. You can also click through the MyController::class . Nice! If you refactor your controller or its namespace, PhpStorm will automatically update the routes file. Those are all big wins!

Also, if you now want to use another namespace for your controller, you can simply do so:

use App \ Front \ Http \ AlternativeNamespace \ MyController ; Route::get( 'my-url' , MyController::class);

In closing #

Laravel tries to make it easy for newcomers by providing some traits to scaffolded controllers by default and by setting up a default namespace. But if you have a bit of experience, I highly recommend not letting your controller extend anything by default and not using a default namespace.

Maybe the next version of Laravel should scaffold a class (without extending anything) as a controller and not use a default namespace. Do you agree? Let me know why or why not in the comments below.