Laravel - Controllers
Laravel - Controllers
Section titled “Laravel - Controllers”Controllers are a fundamental part of the Model-View-Controller (MVC) architectural pattern. In Laravel, controllers are responsible for handling incoming HTTP requests, retrieving or manipulating data (often via Models), and then loading an appropriate View or returning a JSON response.
Basic Controllers
Section titled “Basic Controllers”Instead of defining all your request handling logic as Closures in route files, you may wish to organize this behavior using Controller classes. Controllers can group related HTTP request handling logic into a single class.
Creating a Controller
Section titled “Creating a Controller”You can generate a new controller using the Artisan CLI command:
php artisan make:controller <ControllerName>Replace <ControllerName> with the name of your controller (e.g., UserController, PhotoController). By convention, controller names are singular and end with Controller.
For example, to create a ProductController:
php artisan make:controller ProductControllerThis command will create a new controller file at app/Http/Controllers/ProductController.php. A basic controller looks like this:
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request; // Automatically included
class ProductController extends Controller{ // Methods to handle requests will go here public function index() { // Logic to display a list of products return view('products.index'); // Example: returns a Blade view }
public function show(string $id) { // Logic to display a specific product // Example: Product::findOrFail($id); return view('products.show', ['productId' => $id]); }}Routing to Controller Methods
Section titled “Routing to Controller Methods”Once you have a controller method, you can route to it from your routes/web.php or routes/api.php file:
use App\Http\Controllers\ProductController;
// Routes to the 'index' method in ProductControllerRoute::get('/products', [ProductController::class, 'index']);
// Routes to the 'show' method, passing an 'id' parameterRoute::get('/products/{id}', [ProductController::class, 'show']);This syntax [ControllerName::class, 'methodName'] is the recommended way as it provides better IDE support and refactorability.
Controller Middleware
Section titled “Controller Middleware”Middleware can be assigned to controller routes in your route files, or you can specify middleware within your controller’s constructor or methods. This allows you to apply filters (like authentication or authorization) to specific controller actions.
Assigning Middleware in the Controller’s Constructor:
Section titled “Assigning Middleware in the Controller’s Constructor:”You can use the middleware method within your controller’s constructor to assign middleware to actions within the controller. You can even constrain the middleware to only run for certain methods.
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
class UserProfileController extends Controller{ public function __construct() { // Apply 'auth' middleware to all methods in this controller $this->middleware('auth');
// Apply 'subscribed' middleware only to the 'settings' method $this->middleware('subscribed')->only('settings');
// Apply 'admin' middleware to all methods except 'show' $this->middleware('admin')->except('show'); }
public function show(string $id) { return view('profile.show', ['userId' => $id]); }
public function edit(string $id) { // Only authenticated users can reach this return view('profile.edit', ['userId' => $id]); }
public function settings() { // Only authenticated and subscribed users can reach this return view('profile.settings'); }}Assigning Middleware via Routes:
Section titled “Assigning Middleware via Routes:”Alternatively, and often preferred for clarity, assign middleware directly in your route definitions:
use App\Http\Controllers\UserProfileController;
Route::get('/profile/{id}', [UserProfileController::class, 'show']);
Route::get('/profile/{id}/edit', [UserProfileController::class, 'edit']) ->middleware('auth');
Route::get('/profile/settings', [UserProfileController::class, 'settings']) ->middleware(['auth', 'subscribed']);Resource Controllers
Section titled “Resource Controllers”For applications that manage resources (like photos, posts, products), Laravel’s resource controllers make it easy to build RESTful controllers around these resources. When you create a resource controller, Laravel pre-fills it with methods for common CRUD (Create, Read, Update, Delete) operations.
Creating a Resource Controller:
Section titled “Creating a Resource Controller:”Use the --resource or -r flag with the make:controller command:
php artisan make:controller PhotoController --resourceThis generates app/Http/Controllers/PhotoController.php with methods like index, create, store, show, edit, update, and destroy.
Example PhotoController (simplified stubs):
Section titled “Example PhotoController (simplified stubs):”<?php
namespace App\Http\Controllers;
use App\Models\Photo; // Assuming you have a Photo modeluse Illuminate\Http\Request;use Illuminate\View\View;use Illuminate\Http\RedirectResponse;
class PhotoController extends Controller{ public function index(): View { // $photos = Photo::all(); // return view('photos.index', ['photos' => $photos]); return view('photos.index'); }
public function create(): View { return view('photos.create'); }
public function store(Request $request): RedirectResponse { // $validated = $request->validate(['title' => 'required|string|max:255']); // Photo::create($validated); // return redirect()->route('photos.index')->with('success', 'Photo created!'); return redirect()->route('photos.index'); }
public function show(string $id): View // Or Photo $photo for route model binding { // $photo = Photo::findOrFail($id); // return view('photos.show', ['photo' => $photo]); return view('photos.show', ['photoId' => $id]); }
public function edit(string $id): View // Or Photo $photo { // $photo = Photo::findOrFail($id); // return view('photos.edit', ['photo' => $photo]); return view('photos.edit', ['photoId' => $id]); }
public function update(Request $request, string $id): RedirectResponse // Or Photo $photo { // $photo = Photo::findOrFail($id); // $validated = $request->validate(['title' => 'required|string|max:255']); // $photo->update($validated); // return redirect()->route('photos.show', $photo)->with('success', 'Photo updated!'); return redirect()->route('photos.index'); }
public function destroy(string $id): RedirectResponse // Or Photo $photo { // $photo = Photo::findOrFail($id); // $photo->delete(); // return redirect()->route('photos.index')->with('success', 'Photo deleted!'); return redirect()->route('photos.index'); }}Routing Resource Controllers:
Section titled “Routing Resource Controllers:”To register all the necessary routes for a resource controller, use Route::resource:
// In routes/web.phpuse App\Http\Controllers\PhotoController;
Route::resource('photos', PhotoController::class);This single line creates multiple routes to handle a variety of actions on the resource. Here’s what it typically generates:
| Verb | URI | Action | Route Name |
|---|---|---|---|
| GET | /photos | index | photos.index |
| GET | /photos/create | create | photos.create |
| POST | /photos | store | photos.store |
| GET | /photos/{photo} | show | photos.show |
| GET | /photos/{photo}/edit | edit | photos.edit |
| PUT/PATCH | /photos/{photo} | update | photos.update |
| DELETE | /photos/{photo} | destroy | photos.destroy |
Tip: If you only need a subset of resource routes, use only or except methods: Route::resource('photos', PhotoController::class)->only(['index', 'show']);
Single Action Controllers
Section titled “Single Action Controllers”If a controller handles only a single action, you can create a single action controller by placing a single __invoke method within the controller. This can simplify controllers that have a very specific, singular purpose.
Creating a Single Action Controller:
Section titled “Creating a Single Action Controller:”Use the --invokable or -i flag with make:controller:
php artisan make:controller ProvisionServer --invokableThis generates app/Http/Controllers/ProvisionServer.php with an __invoke method:
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;use Illuminate\Http\Response; // Or other specific response types
class ProvisionServer extends Controller{ /** * Handle the incoming request. */ public function __invoke(Request $request): Response { // Logic to provision a server... return response('Server provisioning started.'); }}Routing to Single Action Controllers:
Section titled “Routing to Single Action Controllers:”When registering routes for single action controllers, you do not need to specify a method:
// In routes/web.phpuse App\Http\Controllers\ProvisionServer;
Route::post('/server/provision', ProvisionServer::class);Dependency Injection in Controllers
Section titled “Dependency Injection in Controllers”Laravel’s service container is used to resolve all Laravel controllers. As a result, you can type-hint any dependencies your controller may need in its constructor or action methods. The dependencies will automatically be resolved and injected.
Constructor Injection:
Section titled “Constructor Injection:”You can type-hint dependencies in the controller’s constructor. Laravel will automatically inject instances of these services.
Example: Injecting a custom service UserService.
// app/Services/UserService.php (Example Service)namespace App\Services;
class UserService{ public function getActiveUsersCount(): int { // Imagine logic to count active users return 150; }}
// app/Http/Controllers/DashboardController.phpnamespace App\Http\Controllers;
use App\Services\UserService;use Illuminate\View\View;
class DashboardController extends Controller{ private UserService $userService;
public function __construct(UserService $userService) { $this->userService = $userService; }
public function index(): View { $activeUsers = $this->userService->getActiveUsersCount(); return view('dashboard.index', ['activeUsers' => $activeUsers]); }}Method Injection:
Section titled “Method Injection:”In addition to constructor injection, you can also type-hint dependencies on your controller’s action methods. This is useful for dependencies that are only needed by a specific action.
Laravel’s Illuminate\Http\Request object is a common example of method injection.
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;use App\Services\OrderProcessorService; // Another example serviceuse Illuminate\Http\JsonResponse;
class OrderController extends Controller{ // Example of method injection for Request and a custom service public function store(Request $request, OrderProcessorService $processor): JsonResponse { $validatedData = $request->validate([ 'product_id' => 'required|exists:products,id', 'quantity' => 'required|integer|min:1', ]);
$order = $processor->createNewOrder($validatedData);
return response()->json(['message' => 'Order created!', 'order_id' => $order->id], 201); }}Practical Application: Building a User Management Module
Imagine you’re building a user management section for an admin panel. You might create a UserManagementController as a resource controller.
index()would list all users (with pagination).create()would show a form to add a new user.store()would validate and save the new user.edit()would show a form to edit an existing user.update()would validate and update the user.destroy()would delete a user. You’d applyauthand perhaps anadminrole middleware to protect these routes. Dependency injection could be used to inject aUserRepositoryorUserServicefor handling data operations.
Common Learning Obstacle: Fat Controllers
A common mistake is putting too much logic directly into controllers (making them “fat”). Try to keep controllers lean by delegating business logic to Service classes, data access to Repositories or Eloquent models, and complex request validation to Form Request classes. Controllers should primarily orchestrate the flow: receive request, call services/models, return response.
Additional Resources:
Section titled “Additional Resources:”For more details, visit the official Laravel documentation on Controllers: https://laravel.com/docs/controllers