1. Conceptos básicos
  2. Controladores

Introducción

En lugar de definir toda tu lógica de manejo de solicitudes como cierres en tus archivos de ruta, puedes organizar este comportamiento utilizando clases de "controlador". Los controladores pueden agrupar la lógica de manejo de solicitudes relacionada en una sola clase. Por ejemplo, una clase UserController podría manejar todas las solicitudes entrantes relacionadas con los usuarios, incluyendo mostrar, crear, actualizar y eliminar usuarios. De forma predeterminada, los controladores se almacenan en el directorio app/Http/Controllers.

Escribir Controladores

Controladores Básicos

Para generar rápidamente un nuevo controlador, puedes ejecutar el comando Artisan make:controller. De forma predeterminada, todos los controladores de tu aplicación se almacenan en el directorio app/Http/Controllers:

php artisan make:controller UserController

Veamos un ejemplo de un controlador básico. Un controlador puede tener cualquier cantidad de métodos públicos que responderán a las solicitudes HTTP entrantes:

<?php
 
namespace App\Http\Controllers;
 
use App\Models\User;
use Illuminate\View\View;
 
class UserController extends Controller
{
/**
* Mostrar el perfil de un usuario dado.
*/
public function show(string $id): View
{
return view('user.profile', [
'user' => User::findOrFail($id)
]);
}
}

Una vez que hayas escrito una clase y un método de controlador, puedes definir una ruta hacia el método del controlador de la siguiente manera:

use App\Http\Controllers\UserController;
 
Route::get('/user/{id}', [UserController::class, 'show']);

Cuando una solicitud entrante coincida con la URI de ruta especificada, se invocará el método show en la clase App\Http\Controllers\UserController y los parámetros de ruta se pasarán al método.

Nota No es necesario que los controladores extiendan una clase base. Sin embargo, no tendrás acceso a funciones convenientes como los métodos middleware y authorize.

Controladores de Acción Única

Si una acción del controlador es particularmente compleja, puede resultar conveniente dedicar una clase de controlador completa a esa única acción. Para lograr esto, puedes definir un único método __invoke dentro del controlador:

<?php
 
namespace App\Http\Controllers;
 
class ProvisionServer extends Controller
{
/**
* Provisionar un nuevo servidor web.
*/
public function __invoke()
{
// ...
}
}

Cuando registres rutas para controladores de acción única, no es necesario especificar un método de controlador. En su lugar, simplemente puedes pasar el nombre del controlador al enrutador:

use App\Http\Controllers\ProvisionServer;
 
Route::post('/server', ProvisionServer::class);

Puedes generar un controlador invocable utilizando la opción --invokable del comando Artisan make:controller:

php artisan make:controller ProvisionServer --invokable

Nota Las plantillas de controladores pueden personalizarse utilizando la publicación de plantillas.

Middleware de Controlador

Middleware puede asignarse a las rutas del controlador en tus archivos de ruta:

Route::get('profile', [UserController::class, 'show'])->middleware('auth');

O puedes encontrar conveniente especificar el middleware dentro del constructor de tu controlador. Utilizando el método middleware dentro del constructor de tu controlador, puedes asignar middleware a las acciones del controlador:

class UserController extends Controller
{
/**
* Instanciar una nueva instancia de controlador.
*/
public function __construct()
{
$this->middleware('auth');
$this->middleware('log')->only('index');
$this->middleware('subscribed')->except('store');
}
}

Los controladores también te permiten registrar middleware usando un cierre. Esto proporciona una forma conveniente de definir un middleware en línea para un solo controlador sin definir toda una clase de middleware:

use Closure;
use Illuminate\Http\Request;
 
$this->middleware(function (Request $request, Closure $next) {
return $next($request);
});

Controladores de Recursos

Si piensas en cada modelo Eloquent en tu aplicación como un "recurso", es típico realizar los mismos conjuntos de acciones contra cada recurso en tu aplicación. Por ejemplo, imagina que tu aplicación contiene un modelo Photo y un modelo Movie. Es probable que los usuarios puedan crear, leer, actualizar o eliminar estos recursos.

Debido a este caso de uso común, el enrutamiento de recursos de Laravel asigna las típicas rutas de creación, lectura, actualización y eliminación ("CRUD") a un controlador con una sola línea de código. Para comenzar, podemos usar la opción --resource del comando Artisan make:controller para crear rápidamente un controlador que maneje estas acciones:

php artisan make:controller PhotoController --resource

Este comando generará un controlador en app/Http/Controllers/PhotoController.php. El controlador contendrá un método para cada una de las operaciones de recurso disponibles. A continuación, puedes registrar una ruta de recurso que apunte al controlador:

use App\Http\Controllers\PhotoController;
 
Route::resource('photos', PhotoController::class);

Esta única declaración de ruta crea múltiples rutas para manejar una variedad de acciones en el recurso. El controlador generado ya tendrá métodos acotados para cada una de estas acciones. Recuerda que siempre puedes obtener una visión general rápida de las rutas de tu aplicación ejecutando el comando Artisan route:list.

Incluso puedes registrar muchos controladores de recursos a la vez pasando un array al método resources:

Route::resources([
'photos' => PhotoController::class,
'posts' => PostController::class,
]);

Acciones Manejadas por Controlador de Recursos

Verbo URI Acción Nombre de Ruta
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

Personalizar Comportamiento de Modelo Faltante

Típicamente, se generará una respuesta HTTP 404 si no se encuentra un modelo de recurso vinculado implícitamente. Sin embargo, puedes personalizar este comportamiento llamando al método missing al definir tu ruta de recurso. El método missing acepta un cierre que se invocará si no se puede encontrar un modelo vinculado implícitamente para ninguna de las rutas del recurso:

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');
});

Modelos Borrados Suavemente

Típicamente, la vinculación implícita de modelos no recuperará modelos que hayan sido eliminados suavemente, y en su lugar devolverá una respuesta HTTP 404. Sin embargo, puedes indicar al framework que permita modelos eliminados suavemente invocando el método withTrashed al definir tu ruta de recurso:

use App\Http\Controllers\PhotoController;
 
Route::resource('photos', PhotoController::class)->withTrashed();

Llamar a withTrashed sin argumentos permitirá modelos eliminados suavemente para las rutas de recursos show, edit y update. Puedes especificar un subconjunto de estas rutas pasando un array al método withTrashed:

Route::resource('photos', PhotoController::class)->withTrashed(['show']);

Especificar el Modelo de Recurso

Si estás utilizando la vinculación implícita de modelos en rutas y deseas que los métodos del controlador de recursos tengan como tipo una instancia del modelo, puedes usar la opción --model al generar el controlador:

php artisan make:controller PhotoController --model=Photo --resource

Generar Solicitudes de Formulario

Puedes proporcionar la opción --requests al generar un controlador de recursos para indicarle a Artisan que genere clases de solicitudes de formulario para los métodos de almacenamiento y actualización del controlador:

php artisan make:controller PhotoController --model=Photo --resource --requests

Rutas de Recursos Parciales

Cuando declaras una ruta de recurso, puedes especificar un subconjunto de acciones que el controlador debe manejar en lugar del conjunto completo de acciones predeterminadas:

use App\Http\Controllers\PhotoController;
 
Route::resource('photos', PhotoController::class)->only([
'index', 'show'
]);
 
Route::resource('photos', PhotoController::class)->except([
'create', 'store', 'update', 'destroy'
]);

Rutas de Recursos API

Al declarar rutas de recursos que serán consumidas por APIs, comúnmente querrás excluir rutas que presenten plantillas HTML como create y edit. Para mayor comodidad, puedes usar el método apiResource para excluir automáticamente estas dos rutas:

use App\Http\Controllers\PhotoController;
 
Route::apiResource('photos', PhotoController::class);

Puedes registrar muchos controladores de recursos API a la vez pasando un array al método apiResources:

use App\Http\Controllers\PhotoController;
use App\Http\Controllers\PostController;
 
Route::apiResources([
'photos' => PhotoController::class,
'posts' => PostController::class,
]);

Para generar rápidamente un controlador de recursos API que no incluya los métodos create o edit, usa el interruptor --api al ejecutar el comando make:controller:

php artisan make:controller PhotoController --api

Recursos Anidados

A veces, es posible que necesites definir rutas para un recurso anidado. Por ejemplo, un recurso de foto puede tener varios comentarios que pueden estar vinculados a la foto. Para anidar los controladores de recursos, puedes usar la notación "punto" en tu declaración de ruta:

use App\Http\Controllers\PhotoCommentController;
 
Route::resource('photos.comments', PhotoCommentController::class);

Esta ruta registrará un recurso anidado que puede ser accedido con URIs como las siguientes:

/photos/{photo}/comments/{comment}

Escopos de Recursos Anidados

La característica de vinculación implícita de modelos de Laravel puede ajustar automáticamente las vinculaciones anidadas de manera que se confirme que el modelo secundario resuelto pertenece al modelo principal. Al usar el método scoped al definir tu recurso anidado, puedes habilitar automáticamente el ajuste y también indicar a Laravel por qué campo se debe recuperar el recurso secundario. Para obtener más información sobre cómo lograr esto, consulta la documentación sobre ajuste de rutas de recursos.

Anidamiento Superficial

A menudo, no es completamente necesario tener tanto los ID del padre como los del hijo dentro de una URI, ya que el ID del hijo ya es un identificador único. Cuando uses identificadores únicos como claves primarias autoincrementales para identificar tus modelos en segmentos de URI, puedes optar por usar el "anidamiento superficial":

use App\Http\Controllers\CommentController;
 
Route::resource('photos.comments', CommentController::class)->shallow();

Esta definición de ruta definirá las siguientes rutas:

Verbo URI Acción Nombre de Ruta
GET /photos/{photo}/comments index photos.comments.index
GET /photos/{photo}/comments/create create photos.comments.create
POST /photos/{photo}/comments store photos.comments.store
GET /comments/{comment} show comments.show
GET /comments/{comment}/edit edit comments.edit
PUT/PATCH /comments/{comment} update comments.update
DELETE /comments/{comment} destroy comments.destroy

Nombrar Rutas de Recursos

De forma predeterminada, todas las acciones del controlador de recursos tienen un nombre de ruta; sin embargo, puedes anular estos nombres pasando un array names con los nombres de ruta deseados:

use App\Http\Controllers\PhotoController;
 
Route::resource('photos', PhotoController::class)->names([
'create' => 'photos.build'
]);

Nombrar Parámetros de Ruta de Recursos

De forma predeterminada, Route::resource creará los parámetros de ruta para las rutas de recursos basándose en la versión "singularizada" del nombre del recurso. Puedes anular fácilmente esto para cada recurso utilizando el método parameters. El array pasado al método parameters debe ser un array asociativo de nombres de recursos y nombres de parámetros:

use App\Http\Controllers\AdminUserController;
 
Route::resource('users', AdminUserController::class)->parameters([
'users' => 'admin_user'
]);

El ejemplo anterior genera la siguiente URI para la ruta show del recurso:

/users/{admin_user}

Escopos de Rutas de Recursos

La función de enlace de modelo implícito con ámbito en Laravel puede acotar automáticamente los enlaces anidados de manera que el modelo secundario resuelto pertenezca al modelo principal. Al utilizar el método scoped al definir tu recurso anidado, puedes habilitar el acotamiento automático e indicar a Laravel por qué campo se debe recuperar el recurso secundario:

use App\Http\Controllers\PhotoCommentController;
 
Route::resource('photos.comments', PhotoCommentController::class)->scoped([
'comment' => 'slug',
]);

Esta ruta registrará un recurso anidado acotado que se puede acceder con URIs como las siguientes:

/photos/{photo}/comments/{comment:slug}

Cuando se utiliza un enlace implícito clave personalizado como parámetro de ruta anidado, Laravel acotará automáticamente la consulta para recuperar el modelo anidado por su padre utilizando convenciones para adivinar el nombre de la relación en el padre. En este caso, se asumirá que el modelo Photo tiene una relación llamada comments (el plural del nombre del parámetro de ruta) que se puede utilizar para recuperar el modelo Comment.

Localización de URIs de Recursos

De forma predeterminada, Route::resource creará URIs de recursos utilizando verbos en inglés y reglas en plural. Si necesitas localizar los verbos de acción create y edit, puedes utilizar el método Route::resourceVerbs. Esto se puede hacer al principio del método boot dentro del App\Providers\RouteServiceProvider de tu aplicación:

/**
* Define tus vinculaciones de modelos de ruta, filtros de patrones, etc.
*/
public function boot(): void
{
Route::resourceVerbs([
'create' => 'crear',
'edit' => 'editar',
]);
 
// ...
}

El pluralizador de Laravel admite varios idiomas que puedes configurar según tus necesidades. Una vez que se han personalizado los verbos y el idioma de pluralización, un registro de ruta de recurso como Route::resource('publicacion', PublicacionController::class) producirá las siguientes URIs:

/publicacion/crear
 
/publicacion/{publicaciones}/editar

Complementar Controladores de Recursos

Si necesitas agregar rutas adicionales a un controlador de recursos más allá del conjunto predeterminado de rutas de recursos, debes definir esas rutas antes de llamar al método Route::resource; de lo contrario, las rutas definidas por el método resource pueden tomar precedencia de manera no intencionada sobre tus rutas complementarias:

use App\Http\Controller\PhotoController;
 
Route::get('/photos/popular', [PhotoController::class, 'popular']);
Route::resource('photos', PhotoController::class);

Nota Recuerda mantener tus controladores enfocados. Si te encuentras necesitando rutinariamente métodos fuera del conjunto típico de acciones de recursos, considera dividir tu controlador en dos, controladores más pequeños.

Controladores de Recursos Singleton

A veces, tu aplicación tendrá recursos que solo pueden tener una única instancia. Por ejemplo, el "perfil" de un usuario se puede editar o actualizar, pero un usuario no puede tener más de un "perfil". De manera similar, una imagen puede tener un solo "thumbnail". Estos recursos se llaman "recursos singleton", lo que significa que solo puede existir una instancia del recurso. En estos escenarios, puedes registrar un controlador de recurso "singleton":

use App\Http\Controllers\ProfileController;
use Illuminate\Support\Facades\Route;
 
Route::singleton('profile', ProfileController::class);

La definición de recurso singleton anterior registrará las siguientes rutas. Como puedes ver, no se registran rutas de "creación" para recursos singleton, y las rutas registradas no aceptan un identificador ya que solo puede existir una instancia del recurso:

Verbo URI Acción Nombre de Ruta
GET /profile show profile.show
GET /profile/edit edit profile.edit
PUT/PATCH /profile update profile.update

Los recursos singleton también se pueden anidar dentro de un recurso estándar:

Route::singleton('photos.thumbnail', ThumbnailController::class);

En este ejemplo, el recurso photos recibiría todas las rutas de recursos estándar; sin embargo, el recurso thumbnail sería un recurso singleton con las siguientes rutas:

Verbo URI Acción Nombre de Ruta
GET /photos/{photo}/thumbnail show photos.thumbnail.show
GET /photos/{photo}/thumbnail/edit edit photos.thumbnail.edit
PUT/PATCH /photos/{photo}/thumbnail update photos.thumbnail.update

Recursos Singleton Creables

Ocasionalmente, es posible que desees definir rutas de creación y almacenamiento para un recurso singleton. Para lograr esto, puedes invocar el método creatable al registrar la ruta de recurso singleton:

Route::singleton('photos.thumbnail', ThumbnailController::class)->creatable();

En este ejemplo, se registrarán las siguientes rutas. Como puedes ver, también se registrará una ruta DELETE para recursos singleton que se pueden crear:

Verbo URI Acción Nombre de Ruta
GET /photos/{photo}/thumbnail/create create photos.thumbnail.create
POST /photos/{photo}/thumbnail store photos.thumbnail.store
GET /photos/{photo}/thumbnail show photos.thumbnail.show
GET /photos/{photo}/thumbnail/edit edit photos.thumbnail.edit
PUT/PATCH /photos/{photo}/thumbnail update photos.thumbnail.update
DELETE /photos/{photo}/thumbnail destroy photos.thumbnail.destroy

Si deseas que Laravel registre la ruta DELETE para un recurso singleton pero no registre las rutas de creación o almacenamiento, puedes utilizar el método destroyable:

Route::singleton(...)->destroyable();

Recursos Singleton API

El método apiSingleton se puede utilizar para registrar un recurso singleton que será manipulado a través de una API, eliminando así la necesidad de las rutas create y edit:

Route::apiSingleton('profile', ProfileController::class);

Por supuesto, los recursos singleton de API también pueden ser creatables, lo que registrará las rutas store y destroy para el recurso:

Route::apiSingleton('photos.thumbnail', ProfileController::class)->creatable();

Inyección de Dependencias y Controladores

Inyección de Constructor

El contenedor de servicios de Laravel se utiliza para resolver todos los controladores de Laravel. Como resultado, puedes hacer referencia de tipo a cualquier dependencia que tu controlador pueda necesitar en su constructor. Las dependencias declaradas se resolverán e inyectarán automáticamente en la instancia del controlador:

<?php
 
namespace App\Http\Controllers;
 
use App\Repositories\UserRepository;
 
class UserController extends Controller
{
/**
* Crear una nueva instancia de controlador.
*/
public function __construct(
protected UserRepository $users,
) {}
}

Inyección de Método

Además de la inyección en el constructor, también puedes hacer referencia de tipo a dependencias en los métodos de tu controlador. Un caso de uso común para la inyección en el método es la inyección de la instancia Illuminate\Http\Request en los métodos de tu controlador:

<?php
 
namespace App\Http\Controllers;
 
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
 
class UserController extends Controller
{
/**
* Almacenar un nuevo usuario.
*/
public function store(Request $request): RedirectResponse
{
$name = $request->name;
 
// Almacenar el usuario...
 
return redirect('/users');
}
}

Si tu método de controlador también espera entrada de un parámetro de ruta, lista tus argumentos de ruta después de tus otras dependencias. Por ejemplo, si tu ruta está definida así:

use App\Http\Controllers\UserController;
 
Route::put('/user/{id}', [UserController::class, 'update']);

Aún puedes hacer referencia de tipo a Illuminate\Http\Request y acceder a tu parámetro id definiendo tu método de controlador de la siguiente manera:

<?php
 
namespace App\Http\Controllers;
 
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
 
class UserController extends Controller
{
/**
* Actualizar el usuario dado.
*/
public function update(Request $request, string $id): RedirectResponse
{
// Actualizar el usuario...
 
return redirect('/users');
}
}