Controllers
Introduction
Instead of defining all of your request handling logic as closures in your route files, you may wish to organize this behavior using "controller" classes. Controllers can group related request handling logic into a single class. For example, a UserController class might handle all incoming requests related to users, including showing, creating, updating, and deleting users. By default, controllers are stored in the app/Http/Controllers directory.
Writing Controllers
Basic Controllers
To quickly generate a new controller, you may run the make:controller Artisan command. By default, all of the controllers for your application are stored in the app/Http/Controllers directory:
php artisan make:controller UserController
Let's take a look at an example of a basic controller. A controller may have any number of public methods which will respond to incoming HTTP requests:
<?php
namespace App\Http\Controllers;
use App\Models\User;
use Illuminate\View\View;
class UserController extends Controller
{
/**
* Show the profile for a given user.
*/
public function show(string $id): View
{
return view('user.profile', [
'user' => User::findOrFail($id)
]);
}
}
Once you have written a controller class and method, you may define a route to the controller method like so:
use App\Http\Controllers\UserController;
Route::get('/user/{id}', [UserController::class, 'show']);
When an incoming request matches the specified route URI, the show method on the App\Http\Controllers\UserController class will be invoked and the route parameters will be passed to the method.
Controllers are not required to extend a base class. However, it is sometimes convenient to extend a base controller class that contains methods that should be shared across all of your controllers.
Single Action Controllers
If a controller action is particularly complex, you might find it convenient to dedicate an entire controller class to that single action. To accomplish this, you may define a single __invoke method within the controller:
<?php
namespace App\Http\Controllers;
class ProvisionServer extends Controller
{
/**
* Provision a new web server.
*/
public function __invoke()
{
// ...
}
}
When registering routes for single action controllers, you do not need to specify a controller method. Instead, you may simply pass the name of the controller to the router:
use App\Http\Controllers\ProvisionServer;
Route::post('/server', ProvisionServer::class);
You may generate an invokable controller by using the --invokable option of the make:controller Artisan command:
php artisan make:controller ProvisionServer --invokable
Controller stubs may be customized using stub publishing.
Controller Middleware
Middleware may be assigned to the controller's routes in your route files:
Route::get('/profile', [UserController::class, 'show'])->middleware('auth');
Or, you may find it convenient to specify middleware within your controller class. To do so, your controller should implement the HasMiddleware interface, which dictates that the controller should have a static middleware method. From this method, you may return an array of middleware that should be applied to the controller's actions:
<?php
namespace App\Http\Controllers;
use Illuminate\Routing\Controllers\HasMiddleware;
use Illuminate\Routing\Controllers\Middleware;
class UserController implements HasMiddleware
{
/**
* Get the middleware that should be assigned to the controller.
*/
public static function middleware(): array
{
return [
'auth',
new Middleware('log', only: ['index']),
new Middleware('subscribed', except: ['store']),
];
}
// ...
}
You may also define controller middleware as closures, which provides a convenient way to define an inline middleware without writing an entire middleware class:
use Closure;
use Illuminate\Http\Request;
/**
* Get the middleware that should be assigned to the controller.
*/
public static function middleware(): array
{
return [
function (Request $request, Closure $next) {
return $next($request);
},
];
}
Controllers implementing Illuminate\Routing\Controllers\HasMiddleware should not extend Illuminate\Routing\Controller.
Resource Controllers
If you think of each Eloquent model in your application as a "resource", it is typical to perform the same sets of actions against each resource in your application. For example, imagine your application contains a Photo model and a Movie model. It is likely that users can create, read, update, or delete these resources.
Because of this common use case, Laravel resource routing assigns the typical create, read, update, and delete ("CRUD") routes to a controller with a single line of code. To get started, we can use the make:controller Artisan command's --resource option to quickly create a controller to handle these actions:
php artisan make:controller PhotoController --resource
This command will generate a controller at app/Http/Controllers/PhotoController.php. The controller will contain a method for each of the available resource operations. Next, you may register a resource route that points to the controller:
use App\Http\Controllers\PhotoController;
Route::resource('photos', PhotoController::class);
This single route declaration creates multiple routes to handle a variety of actions on the resource. The generated controller will already have methods stubbed for each of these actions. Remember, you can always get a quick overview of your application's routes by running the route:list Artisan command.
You may even register many resource controllers at once by passing an array to the resources method:
Route::resources([
'photos' => PhotoController::class,
'posts' => PostController::class,
]);
Actions Handled by Resource Controllers
| 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 |
Customizing Missing Model Behavior
Typically, a 404 HTTP response will be generated if an implicitly bound resource model is not found. However, you may customize this behavior by calling the missing method when defining your resource route. The missing method accepts a closure that will be invoked if an implicitly bound model cannot be found for any of the resource's routes:
use App\Http\Controllers\PhotoController;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Redirect;
Route::resource('photos', PhotoController::class)
->missing(function (Request $request) {
return Redirect::route('photos.index');
});
Soft Deleted Models
Typically, implicit model binding will not retrieve models that have been soft deleted, and will instead return a 404 HTTP response. However, you can instruct the framework to allow soft deleted models by invoking the withTrashed method when defining your resource route:
use App\Http\Controllers\PhotoController;
Route::resource('photos', PhotoController::class)->withTrashed();
Calling withTrashed with no arguments will allow soft deleted models for the show, edit, and update resource routes. You may specify a subset of these routes by passing an array to the withTrashed method:
Route::resource('photos', PhotoController::class)->withTrashed(['show']);
Specifying the Resource Model
If you are using route model binding and would like the resource controller's methods to type-hint a model instance, you may use the --model option when generating the controller:
php artisan make:controller PhotoController --model=Photo --resource
Generating Form Requests
You may provide the --requests option when generating a resource controller to instruct Artisan to generate form request classes for the controller's storage and update methods:
php artisan make:controller PhotoController --model=Photo --resource --requests
Partial Resource Routes
When declaring a resource route, you may specify a subset of actions the controller should handle instead of the full set of default actions:
use App\Http\Controllers\PhotoController;
Route::resource('photos', PhotoController::class)->only([
'index', 'show'
]);
Route::resource('photos', PhotoController::class)->except([
'create', 'store', 'update', 'destroy'
]);
API Resource Routes
When declaring resource routes that will be consumed by APIs, you will commonly want to exclude routes that present HTML templates such as create and edit. For convenience, you may use the apiResource method to automatically exclude these two routes:
use App\Http\Controllers\PhotoController;
Route::apiResource('photos', PhotoController::class);
You may register many API resource controllers at once by passing an array to the apiResources method:
use App\Http\Controllers\PhotoController;
use App\Http\Controllers\PostController;
Route::apiResources([
'photos' => PhotoController::class,
'posts' => PostController::class,
]);
To quickly generate an API resource controller that does not include the create or edit methods, use the --api switch when executing the make:controller command:
php artisan make:controller PhotoController --api
Nested Resources
Sometimes you may need to define routes to a nested resource. For example, a photo resource may have multiple comments that may be attached to the photo. To nest the resource controllers, you may use "dot" notation in your route declaration:
use App\Http\Controllers\PhotoCommentController;
Route::resource('photos.comments', PhotoCommentController::class);
This route will register a nested resource that may be accessed with URIs like the following:
/photos/{photo}/comments/{comment}