Using Single Action Classes in Laravel Controller

Using Single Action Classes in Laravel Controller

Laravel Controller is the core element of MVC (Model View Controller) design pattern, but they can grow to an unmanageable size as your application becomes bigger. As a developer, I always have to think where I should put my logic and keep my code DRY(Don’t Repeat Yourself).

Problem

Writing logic in your controller is perfectly fine when you know you have only one endpoint. Today’s modern application development, it’s very common that you have to deal with several endpoints while sharing the same functionalities.

So you have to write multiple methods to return the correct response.

Also, I have seen often, controllers with a huge list of dependency classes injected, which makes a controller very big and hard to manage. Laravel provide the service container which will resolve the dependencies and inject them if they get resolved. But the issue is still there, our controller is still getting messy.

Recently I have encountered in one of my Project, even with minimum class injection, a controller working on a single model can still become very large.

Taking an example from my project, I have a Movie model and a MovieController, in my controller I have many methods like storeMovie, searchMovie, storeActorsForMovie, loadMovieImages, uploadMovieImages, and the list goes on.

I can split them into different controllers, but how?. I have to think about which part I have to split and into where?

Solution

One solution I have found is using invokable classes, using the magic method __invoke(). By creating a single action class, I have to write only __constructor() and __invoke() method to perform a single action.

__invoke() is a PHP magic function and when we will create an instance of the class or call this class __invoke() will be loaded automatically. By using this approach, we are also following SRP (Single Responsibility Principle). SRP means any class should have one reason to change. This makes our application more robust and testable and you don’t have to inject every dependency for every method and can return the response needed.

By creating a single action controller in Laravel, you have to name the controller to be more indicative of the action it will be performing. You also have to spend less time to find a particular method from a single large controller.

So for my MovieController, I can split them into SearchMovieController, LoadMovieImagesController, UploadMovieImagesController and so on. You can see all these names now become more descriptive and we know they only have one action to perform using __invoke() method.

Now my routes will become from:

Route::post('searchMovie', 'MovieController@searchMovie');
    Route::get('loadMoviesImages', 'MovieController@loadMovieImages');

to this:

Route::post('searchMovie', 'SearchMovieController');
    Route::get('loadMoviesImages', 'LoadMovieImagesController');

Now, SearchMovieController will look like this:

namespace App\Http\Controllers;

use Illuminate\Http\Request;

class SearchMovieController extends Controller
{
    public function __invoke()
    {
        // do movie search here
    }
}

Similarly, I can create all controllers for just one specific action.

Drawbacks

Above example is perfectly fine for using a single action class based controller to keep code clean and DRY, but there are some limitations of this approach.

If we use this approach, we cannot use the resource routes like Route::resource();. You have to define every route of your resource and a corresponding controller.

If you have any question or suggestion to improve this, you can leave that in the comment box below.

Your little help will keep this site alive and help us to produce quality content for you.