# Introducción

En lugar de definir toda la lógica para la gestión de una petición dentro de Closures o funciones anónimas en los archivos de rutas, se puede organizar este comportamiento en unas clases llamadas Controladores (controllers). Los controladores pueden agrupar la lógica de gestión de peticiones relacionadas en una única clase. Estos controladores se encuentran normalmente en el directorio app/Http/Controllers.

# Controlladores básicos

Definir controlladores

A continuación se muestra un ejemplo de una clase de controlador básico. Tenga en cuenta que el controlador hereda de la clase de controlador base incluida con Laravel. La clase base provee de una serie de métodos útiles como el método middleware, que se puede usar para adjuntar un middleware a las acciones del controlador

<?php

namespace App\Http\Controllers;

use App\Http\Controllers\Controller;
use App\Models\User;

class UserController extends Controller
{
    /**
     * Show the profile for the given user.
     *
     * @param  int  $id
     * @return \Illuminate\View\View
     */
    public function show($id)
    {
        return view('user.profile', ['user' => User::findOrFail($id)]);
    }
}

Puedes definir una ruta para esta acción del controlador así:

use App\Http\Controllers\UserController;

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

Ahora, cuando una petición concuerda con la URI de la ruta, se ejecutará el método show de la clase UserController. Por supuesto, los parámetros de la ruta se pasarán también a este método.

Los controladores no requieren heredar la clase base. Sin embargo, no se tendrá acceso a las características como los métodos middlewarevalidate, y dispatch.

Controlladores de acción simple

Si desea definir un controlador que sólo maneje una única acción, puede colocar un único método __invoke en el controlador:

<?php

namespace App\Http\Controllers;

use App\Http\Controllers\Controller;
use App\Models\User;

class ShowProfile extends Controller
{
    /**
     * Show the profile for the given user.
     *
     * @param  int  $id
     * @return \Illuminate\View\View
     */
    public function __invoke($id)
    {
        return view('user.profile', ['user' => User::findOrFail($id)]);
    }
}

Cuando se registran las rutas para los controladores de acción única, no es necesario especificar un método:

use App\Http\Controllers\ShowProfile;

Route::get('user/{id}', ShowProfile::class);

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

php artisan make:controller ShowProfile --invokable

Los talones de los controladores pueden personalizarse mediante la stub publishing

# Controller Middleware

A las rutas de los controladores se les puede asignar middleware del siguiente modo:

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

Sin embargo, es más conveniente especificar el middleware en el constructor del controlador. Utilizando el método middleware desde el constructor del controlador, se puede asignar un middleware a las acciones del controlador. Incluso se puede restringir el middleware a únicamente ciertos métodos:

class UserController extends Controller
{
    /**
     * Instantiate a new controller instance.
     *
     * @return void
     */
    public function __construct()
    {
        $this->middleware('auth');

        $this->middleware('log')->only('index');

        $this->middleware('subscribed')->except('store');
    }
}

Los controladores también permiten registrar middleware usando un Closure o función anónima. Esto proporciona una forma conveniente de definir un middleware para un solo controlador sin definir una clase middleware completa:

$this->middleware(function ($request, $next) {
    // ...

    return $next($request);
});

Puede asignar el middleware a un subconjunto de acciones del controlador; sin embargo, puede indicar que el controlador está creciendo demasiado. En su lugar, considere la posibilidad de dividir el controlador en varios controladores más pequeños.

# Controlador de recursos (Resource Controllers)

El routing de recursos de Laravel asigna las rutas "CRUD" típicas a un controlador con una sola línea de código. Por ejemplo, la creación de un controlador que gestiona todas las peticiones HTTP sobre "photos" (fotos) almacenadas por nuestra aplicación. Utilizando el comando de Artisan make:controller, se puede crear un controlador rápidamente:

php artisan make:controller PhotoController --resource

El comando generará un controlador en el archivo app/Http/Controllers/PhotoController.php. El controlador incluirá un método para cada una de las operaciones disponibles para el recurso.

A continuación, se puede registrar una ruta de recursos para el controlador:

Route::resource('photos', PhotoController::class);

Esta declaración de ruta única crea múltiples rutas para manejar una variedad de acciones sobre el recurso. El controlador generado ya tendrá métodos comprobados para cada una de estas acciones, incluyendo notas que le informen de los verbos HTTP y URI que manejan.

Se pueden registrar varios controladores de recursos a la vez pasando una array al método resources:

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

Acciones gestionadas por controladores 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

Especificar el modelo del recurso

Si se está utilizando el route model binding y se desea que los métodos del controlador de recursos incluyan un type-hint de una instancia del modelo, se puede usar la opción --model al generar el controlador:

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

Rutas de recursos parciales

Al declarar una ruta de recursos, puede especificar un subconjunto de acciones que el controlador debe manejar en lugar del conjunto completo de acciones predeterminadas:

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

Route::resource('photos', PhotoController::class)->except([
    'create', 'store', 'update', 'destroy'
]);

Rutas de Recursos de la API

Al declarar las rutas de recursos que serán consumidas por las API, comúnmente querrá excluir las rutas que presenten plantillas HTML, como las de createedit. Para mayor comodidad, puede utilizar el método apiResource para excluir automáticamente estas dos rutas:

Route::apiResource('photos', PhotoController::class);

Puede registrar muchos controladores de recursos de la API a la vez pasando una matriz al método apiResources:

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

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

php artisan make:controller API/PhotoController --api

Recursos anidados

A veces puede ser necesario definir rutas a un recurso anidado. Por ejemplo, un recurso fotográfico puede tener múltiples comentarios que pueden ser adjuntados a la foto. Para anidar los controladores de recursos, utilice la notación "punto" en la declaración de la ruta:

Route::resource('photos.comments', PhotoCommentController::class);

Esta ruta registrará un recurso anidado al que se podrá acceder con URIs como el siguiente:

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

Alcance de recursos anidados

La característica de implicit model binding de Laravel puede abarcar automáticamente las vinculaciones anidadas de manera que se confirme que el modelo hijo resuelto pertenece al modelo padre. Utilizando el método scoped cuando se define el recurso anidado, puede habilitar el alcance automático así como indicar a Laravel por qué campo debe ser recuperado el recurso hijo:

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

Esta ruta registrará un recurso anidado de alcance al que se podrá acceder con URIs como el siguiente:

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

Anidación superficial

A menudo, no es del todo necesario tener tanto la identificación del padre como la del niño dentro de una URI, ya que la identificación del niño ya es un identificador único. Cuando se utilizan identificadores únicos, como las claves primarias de autoincremento para identificar sus modelos en los segmentos de la URI, se puede optar por utilizar "anidación superficial":

Route::resource('photos.comments', CommentController::class)->shallow();

The route definition above will define the following routes:

Verb URI Action Route Name
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 las rutas de recursos

Por defecto, todas las acciones de los controladores de recursos tienen un nombre de ruta; sin embargo, se puede sobrescribir este nombre pasando un array names con sus opciones:

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

Nombrar parámetros en rutas de recursos

Por defecto, Route::resource crea los parámetros de ruta para las rutas de recursos utilizando la versión "singular" del nombre del recurso. Se puede sobrescribir esto fácilmente por recurso pasando parameters en el array de opciones. El array de parameters debe ser un array asociativo de los nombres de los recursos y el nombre de su parámetro:

Route::resource('users', AdminUserController::class)->parameters([
    'users' => 'admin_user'
]);

El ejemplo anterior genera las siguientes URIs para la ruta show del recurso:

/users/{admin_user}

Rutas de recursos de exploración

A veces, al vincular implícitamente múltiples modelos elocuentes en las definiciones de rutas de recursos, se puede desear ampliar el alcance del segundo modelo elocuente de tal manera que debe ser un hijo del primer modelo elocuente. Por ejemplo, considere esta situación que recupera una entrada de blog por babosa para un usuario específico:

use App\Http\Controllers\PostsController;

Route::resource('users.posts', PostsController::class)->scoped();

Puede anular las claves de ruta del modelo por defecto pasando una matriz al método scoped:

use App\Http\Controllers\PostsController;

Route::resource('users.posts', PostsController::class)->scoped([
    'post' => 'slug',
]);

Cuando se utiliza un enlace implícito con clave personalizada como parámetro de ruta anidada, Laravel automáticamente ampliará la consulta para recuperar el modelo anidado por su progenitor utilizando convenciones para adivinar el nombre de la relación en el progenitor. En este caso, se asumirá que el modelo User tiene una relación denominada posts (el plural del nombre del parámetro de ruta) que puede utilizarse para recuperar el modelo Post.

Traducir la URL de los recursos

Por defecto, Route::resource creará URIs de recursos usando verbos en inglés. Si necesitas localizar los verbos de acción de crear y editar, puedes usar el método  Route::resourceVerbs Esto se puede hacer en el método boot de su AppServiceProvider:

use Illuminate\Support\Facades\Route;

/**
 * Bootstrap any application services.
 *
 * @return void
 */
public function boot()
{
    Route::resourceVerbs([
        'create' => 'crear',
        'edit' => 'editar',
    ]);
}

Una vez que los verbos han sido personalizados, un registro de ruta de recursos como Route::resource('fotos', 'PhotoController') producirá las siguientes URIs:

/fotos/crear

/fotos/{foto}/editar

Complementar los controlladores de recursos

Si necesita añadir rutas adicionales a un controlador de recursos más allá del conjunto predeterminado de rutas de recursos, debería definir esas rutas antes de su llamada a Route::resource; de lo contrario, las rutas definidas por el método "resource" pueden tener prioridad involuntariamente sobre sus rutas suplementarias:pueden tener prioridad involuntariamente sobre sus rutas suplementarias:

Route::get('photos/popular', [PhotoController::class, 'popular']);

Route::resource('photos', PhotoController::class);

Recuerden mantener sus controles enfocados. Si se encuentra con que necesita rutinariamente métodos fuera del típico conjunto de acciones de recursos, considere la posibilidad de dividir su controlador en dos controladores más pequeños.

# Inyección de dependencias & Controlladores

Inyección en constructores

El service container de Laravel se utiliza para resolver todos los controladores. Como resultado, se puede type-hint (firma del método) cualquier dependencia que el controlador pueda tener en su constructor. Las dependencias declaradas se resuelven automáticamente y se inyectan en la instancia del controlador:

<?php

namespace App\Http\Controllers;

use App\Repositories\UserRepository;

class UserController extends Controller
{
    /**
     * The user repository instance.
     */
    protected $users;

    /**
     * Create a new controller instance.
     *
     * @param  UserRepository  $users
     * @return void
     */
    public function __construct(UserRepository $users)
    {
        $this->users = $users;
    }
}

Y por supuesto, se puede incluir cualquier Contracto de Laravel. Si el contenedor puede resolverlo, se puede utilizar en la firma del constructor. Dependiendo de la aplicación, inyectar las dependencias dentro del controlador puede ofrecer mejor control sobre el testing.

Inyección en métodos

Además de inyectar en constructores, se puede hacer type-hint de dependencias en los métodos del controlador. Un caso de uso muy común para la inyección es la de la instancia de Illuminate\Http\Request dentro de los métodos del controlador:

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;

class UserController extends Controller
{
    /**
     * Store a new user.
     *
     * @param  Request  $request
     * @return Response
     */
    public function store(Request $request)
    {
        $name = $request->name;

        //
    }
}

Si el método del controlador también espera datos de entrada de un parámetro en la ruta se deben listar los parámetros de ruta después de las otras dependencias. Por ejemplo, si la ruta está definida así:

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

Se podría hacer type-hint de Illuminate\Http\Request y acceder al parámetro de ruta id definiendo el método del controlador de la siguiente forma:

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;

class UserController extends Controller
{
    /**
     * Update the given user.
     *
     * @param  Request  $request
     * @param  string  $id
     * @return Response
     */
    public function update(Request $request, $id)
    {
        //
    }
}

# Caché de rutas

Si la aplicación usa exclusivamente rutas basadas en controladores, se puede aprovechar la caché de rutas de Laravel. Utilizando la caché de rutas se reducirá drásticamente el tiempo que toma la aplicación en registrar todas las rutas. En algunos casos, ¡el registro de rutas puede ser hasta 100x más rápido. Para generar una caché de rutas, simplemente hay que ejecutar el comando de Artisan route:cache:

php artisan route:cache

Tras ejecutar el comando, el archivo de rutas en caché se cargará en cada solicitud. Recordar, si se añaden nuevas rutas se debe generar de nuevo una nueva caché. Es por esto, se recomienda utilizar el comando route:cache únicamente en el entorno de producción.

Se puede utilizar el comando route:clear para limpiar las rutas en caché:

php artisan route:clear