Laravel Core

Invokable Single Action Controllers in Laravel

Saturday, November 30, 2024

// 2 min read

Laravel 11 Controller
Laravel Invokable Controller

A single action controller in Laravel is a controller that does one job. It is particularly useful to have this type of controller if your route needs to handle complex logic.

The invoke method

The single action controller uses PHP's __invoke magic method. If a class has an invoke method, you can call the class as an object.

For example:

<?php

class Greeting
{
    public function __invoke($name)
    {
        return "Hello, $name! ";
    }
}

// Create an instance of the class
$greeting = new Greeting();

// Call the object as a function
echo $greeting("Gergory");  // Outputs: Hello, Gergory!

?>

Creating a single action Controller

Let's assume that in your application, the user has an option to delete his/her account. Upon deletion, there are a couple of jobs that your application must run:

  • send a notification email about account deletion

  • clean up any user-related data

  • delete the user

  • finally, log out the user

You may use the make:controller artisan command with the --invokable option to generate a single action controller.

php artisan make:controller DeleteUserAccountController --invokable

This creates a controller file with the __invoke method:

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;

class DeleteUserAccountController extends Controller
{
    /**
     * Handle the incoming request.
     */
    public function __invoke(Request $request)
    {
        //
    }
}

After adding the necessary scripts for each step, this is how my controller looks like:

<?php

namespace App\Http\Controllers;

use App\Models\User;
use App\Mail\AccountDeleted;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Mail;

class DeleteUserAccountController extends Controller
{
    public function __invoke(Request $request)
    {
        // Validate confirmation input
        $request->validate([
            'confirm' => 'required|boolean', // Ensure a confirm checkbox is checked
        ]);

        /** @var User */
        $user = Auth::user();

        // Send a notification email about the account deletion
        Mail::to($user->email)->send(new AccountDeleted($user));

        // Log the user out
        Auth::logout();

        // Invalidate the session
        $request->session()->invalidate();

        // Regenerate the session token
        $request->session()->regenerateToken();

        // Perform user-related cleanup tasks (if required)
        $user->cleanUpData();

        // Delete the user account
        $user->delete();

        // Redirect to the front page with a flash message
        return redirect('/')
            ->with('status', 'Your account has been deleted successfully.');
    }
}

Single action Route

Now you need to create your route, in the case of a single action controller, you only need to reference the class name without specifying the controller action.

Route::delete('/delete-account', App\Http\Controllers\DeleteUserAccountController::class)
    ->middleware('auth')
    ->name('delete-account');

You can assign middleware, give it a name, and add route parameters as you normally do with other routes. Because this route is only available for logged-in users, I added the auth middleware.

Advantages of Single Action Controllers

  • simplicity: it only does one job, this way makes the code more focused and easier to understand

  • maintainability: for the same reason, it is easy to change and maintain

  • testability: and simple to test

Disadvantages of Single Action Controller

A Single Action Controller offers numerous advantages, but they are not always the best choice for every scenario.

  • increased files: if each action requires its own controller, that leads to more files in the project. It is recommended to only use this type of controller for more complex route actions.

  • reduced conceptual unity: grouping related methods in one controller provides a sense of context and cohesion. For instance, all user-related CRUD operations in a Resource Controller with index, store, update, and destroy methods make more sense than using four separate invoke controllers.

If you want to support my work, you can donate using the button below. Thank you!