Laravel Controllers 101: A Comprehensive Guide

Samuel AgyeiSamuel Agyei
Sep 26, 2024·
7 min read

Laravel is a PHP web framework known for its elegant syntax and powerful tools. One of its core components is the Controller, which helps manage the application logic, route handling, and data flow. Controllers serve as a crucial bridge between the user and the system, ensuring the separation of concerns, making code more maintainable, and enabling clean architecture.

In this post, we’ll dive deep into Laravel Controllers, explaining their purpose, types, usage, and best practices. We will explore creating controllers, working with resource controllers, route binding, middleware, and other advanced techniques.


Table of Contents

  1. What is a Laravel Controller?

  2. Creating a Basic Controller

  3. Single-Action Controllers

  4. Resource Controllers

  5. Working with Route Parameters

  6. Route Model Binding

  7. Using Middleware in Controllers

  8. Controller Best Practices

  9. Conclusion


1. What is a Laravel Controller?

A controller in Laravel is a class that handles the incoming HTTP requests and returns responses. Instead of defining all logic inside routes (which would become unmanageable in large applications), Laravel encourages the use of controllers to keep the code modular, readable, and maintainable.

Key responsibilities of controllers:

  • Processing HTTP requests

  • Retrieving or manipulating data from models

  • Passing data to views

  • Handling business logic for routes

For instance, a UserController might handle user-related requests such as showing user data, updating profiles, or managing authentication.

2. Creating a Basic Controller

In Laravel, you can create a controller using the Artisan command-line tool. Let’s start by generating a basic controller.

Generating a Controller

To create a controller, run the following Artisan command:

php artisan make:controller UserController

This creates a new file named UserController.php in the app/Http/Controllers directory. It looks like this:

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;

class UserController extends Controller
{
    //
}

Adding Methods to the Controller

Once the controller is generated, you can define methods for handling different types of requests. Let’s add some basic actions, like showing a user profile and updating user data:

class UserController extends Controller
{
    // Show user profile
    public function show($id)
    {
        $user = User::find($id);
        return view('user.profile', ['user' => $user]);
    }

    // Update user data
    public function update(Request $request, $id)
    {
        $user = User::find($id);
        $user->update($request->all());
        return redirect()->back()->with('status', 'Profile updated!');
    }
}

Defining Routes

Now, you can define routes in routes/web.php to use the methods in your UserController:

use App\Http\Controllers\UserController;

Route::get('/user/{id}', [UserController::class, 'show']);
Route::post('/user/{id}/update', [UserController::class, 'update']);

3. Single-Action Controllers

Sometimes, a controller only needs to handle a single action. For example, if you’re only handling a single page or a single form submission, using a controller with multiple methods might be overkill.

Laravel allows you to create single-action controllers using the __invoke method.

Creating a Single-Action Controller

To generate a single-action controller, run:

php artisan make:controller ContactController --invokable

The generated controller looks like this:

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;

class ContactController extends Controller
{
    public function __invoke(Request $request)
    {
        // Handle the contact form submission
        return view('contact');
    }
}

Defining the Route

Since the controller only has one action, you don’t need to specify the method in the route. Laravel will automatically invoke the __invoke method:

Route::get('/contact', ContactController::class);

4. Resource Controllers

Resource controllers provide a way to generate controllers that manage standard CRUD (Create, Read, Update, Delete) operations. Laravel’s resource controllers follow RESTful principles, making it easy to define routes for common actions like listing resources, showing a single resource, creating, updating, and deleting.

Generating a Resource Controller

To create a resource controller, run:

php artisan make:controller ProductController --resource

This generates a controller with the following methods:

  • index(): Display a listing of the resource.

  • create(): Show the form for creating a new resource.

  • store(): Store a newly created resource in storage.

  • show(): Display the specified resource.

  • edit(): Show the form for editing the specified resource.

  • update(): Update the specified resource in storage.

  • destroy(): Remove the specified resource from storage.

Defining Resource Routes

You can register a resource controller in routes/web.php using the Route::resource() method:

use App\Http\Controllers\ProductController;

Route::resource('products', ProductController::class);

This will automatically create all the necessary routes for the controller actions. You can check the routes by running:

php artisan route:list

Customizing Resource Controller Routes

If you don’t need all of the routes, you can limit them like this:

Route::resource('products', ProductController::class)->only(['index', 'show']);

Or, exclude specific routes:

Route::resource('products', ProductController::class)->except(['create', 'edit']);

5. Working with Route Parameters

Laravel controllers support route parameters, which you can capture directly in controller methods.

Basic Route Parameters

Here’s an example of how to use route parameters in a controller:

class ProductController extends Controller
{
    public function show($id)
    {
        $product = Product::findOrFail($id);
        return view('products.show', ['product' => $product]);
    }
}

In routes/web.php:

Route::get('/products/{id}', [ProductController::class, 'show']);

Optional Route Parameters

Sometimes you want to make a route parameter optional. In such cases, you can add a ? to the parameter name and provide a default value in the controller:

class ProductController extends Controller
{
    public function show($id = null)
    {
        if ($id) {
            $product = Product::findOrFail($id);
            return view('products.show', ['product' => $product]);
        }

        return view('products.index');
    }
}

In routes/web.php:

Route::get('/products/{id?}', [ProductController::class, 'show']);

6. Route Model Binding

Route model binding is a feature in Laravel that automatically resolves route parameters into model instances. This eliminates the need to manually fetch models from the database in your controller methods.

Implicit Binding

With implicit binding, Laravel automatically resolves a route parameter to a model instance. You can define it like this:

class ProductController extends Controller
{
    public function show(Product $product)
    {
        return view('products.show', ['product' => $product]);
    }
}

In routes/web.php:

Route::get('/products/{product}', [ProductController::class, 'show']);

When the route is accessed with a product ID, Laravel automatically retrieves the product instance from the database.

Customizing the Key for Implicit Binding

By default, Laravel uses the model’s primary key for implicit binding. You can customize it by overriding the getRouteKeyName() method in the model:

class Product extends Model
{
    public function getRouteKeyName()
    {
        return 'slug';
    }
}

Now, Laravel will bind the route using the slug field instead of the primary key.

7. Using Middleware in Controllers

Middleware in Laravel filters HTTP requests. You can apply middleware to controllers or specific methods to control access, validate input, or perform other tasks before the request reaches the controller logic.

Applying Middleware to Controllers

You can apply middleware in the controller’s constructor:

class UserController extends Controller
{
    public function __construct()
    {
        $this->middleware('auth');
    }

    public function profile()
    {
        return view('profile');
    }
}

This ensures that only authenticated users can access the profile method.

Applying Middleware to Specific Methods

If you want to apply middleware to specific methods, you can pass an array of methods to the middleware:

class UserController extends Controller
{
    public function __construct()
    {
        $this->middleware('auth')->only(['profile', 'edit']);
    }
}

Alternatively, you can exclude specific methods from middleware:

class UserController extends Controller
{
    public function __construct()
    {
        $this->middleware('auth')->except(['index']);
    }
}

8. Controller Best Practices

When working with controllers in Laravel, here are some best practices to follow:

  1. Keep Controllers Thin: Controllers should focus on handling requests, delegating business logic to services or models. Avoid placing too much logic inside controllers.

  2. Use Dependency Injection: Laravel’s IoC container allows you to inject dependencies directly into your controller methods, making your code more testable and modular.

  3. Leverage Route Model Binding: Use route model binding to simplify fetching model instances, reducing repetitive code.

  4. Organize Controllers by Domain: If your application grows, organize your controllers into subdirectories to maintain a clean structure. For example, group controllers into Admin, User, or API namespaces.

  5. Use Single-Action Controllers When Appropriate: For simple actions, like handling a single form submission, use single-action controllers to keep your code concise and clear.

  6. Keep Route Definitions Simple: Instead of writing complex route logic inside routes, delegate it to controllers. This makes your routes file cleaner and more manageable.

  7. Apply Middleware at the Controller Level: Apply middleware in controllers when possible, as it keeps your routes cleaner and groups related logic.


9. Conclusion

Laravel Controllers are a powerful tool for managing application logic, enabling cleaner, modular code. Whether working with single-action controllers, resource controllers, or route model binding, understanding how to effectively use controllers will significantly enhance the architecture of your Laravel applications.

By adhering to the best practices and leveraging Laravel's robust routing system, you can create scalable, maintainable, and efficient web applications.

25
Subscribe to my newsletter

Read articles from Samuel Agyei directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Samuel Agyei
Samuel Agyei

I am a full-stack developer. I am passionate about programming in general. I love to read documentaries and code