★ Simplifying controllers
Tags: laravel, php
Published: 2020-01-02 23:21:20

---
title: ★ Simplifying controllers
published: true
date: 2019-04-23 19:33:48 UTC
tags: laravel, php
canonical_url: https://freek.dev/1324-simplifying-controllers
---

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`:

```php
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.

```php
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.

```php
namespace App\Http\Controllers;

class MyController
{
   // just add some functions
}
```

In all our client projects at [Spatie](https://spatie.be) 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.

```php
namespace App\Providers;

use Illuminate\Support\Facades\Route;
use Illuminate\Foundation\Support\Providers\RouteServiceProvider as ServiceProvider;

class RouteServiceProvider extends ServiceProvider
{
    /**
     * This namespace is applied to your controller routes.
     *
     * In addition, it is set as the URL generator's root namespace.
     *
     * @var string
     */
    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`:

```php
// in the web routes file

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.

```php
// in the web 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.

```php
// in the web routes file

use App\Http\Controllers\MyController;

// isn't this beautiful?
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:

```php
// in the web routes file

// alternative namespace
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.