Controllers
Controllers
En lugar de definir toda la lógica en funciones anónimas en las rutas, puedes organizar este comportamiento utilizando clases llamadas “controladores”. Los controladores agrupan la lógica de manejo de solicitudes en una sola clase. Por ejemplo, una clase UserController podría manejar todas las solicitudes relacionadas con usuarios, incluyendo mostrar, crear, actualizar y eliminar usuarios. Por defecto, los controladores se almacenan en el directorio app/Http/Controllers.
Writing Controllers
Basic controllers
Para generar un controlador basico ejecutar:
php artisan make:controller NewController
Ejemplo básico de un controlador:
<?php
namespace App\Http\Controllers;
use App\Models\User;
use Illuminate\View\View;
class UserController extends Controller
{
public function show(string $id): View
{
return view('user.profile', [
'user' => User::findOrFail($id)
]);
}
}
Una vez creado el controlador, debes definir la ruta la cual accederá a este controlador
use App\Http\Controllers\UserController;
Route::get('/user/{id}', [UserController::class, 'show']);
Cuando una request entrante coincida con el URI de la ruta, el método show será invocado, y los parámetros serán pasados al método del controlador.
ℹ️ No es requerido que los controladores extiendan de una clase base, pero aveces es conveniente que extiendan de un controlador base que contiene métodos que deberán compartir todos los controladores
Single Action Controllers
Cuando la lógica de un controlador es muy compleja, quizás es necesario dedicar la clase entera para esta lógica, por lo que se puede definir un único método _ _invoke y cuando se registre el controlador en una ruta, no es necesario especificar ningún nombre de metodo
<?php
namespace App\Http\Controllers;
class ProvisionServer extends Controller
{
/**
* Provision a new web server.
*/
public function __invoke()
{
// ...
}
}
use App\Http\Controllers\ProvisionServer;
Route::post('/server', ProvisionServer::class);
Puedes generar un controlador con un unico metodo invoke usando la opcion --invokable
php artisan make:controller ProvisionServer --invokable
Controller Middleware
Los controladores pueden ser asignados a la ruta usando dentro de los archivos de rutas:
Route::get('/profile', [UserController::class, 'show'])->middleware('auth');
Por otro lado, quizás quieras especificar el middleware dentro de la propia clase del controlador, para ello deberas implementar la interfaz HasMiddleware el cual indica que tienes que tener un método middleware , desde este método puedes devolver un array con todos los middleware que son aplicables
<?php
namespace App\Http\Controllers;
use App\Http\Controllers\Controller;
use Illuminate\Routing\Controllers\HasMiddleware;
use Illuminate\Routing\Controllers\Middleware;
class UserController extends Controller 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']),
];
}
// ...
}
También se puede definir closures para aplicar inlines middlewares.
use Closure;
use Illuminate\Http\Request;
/**
* Get the middleware that should be assigned to the controller.
*/
public static function middleware(): array
{
return [
// inline middleware
function (Request $request, Closure $next) {
return $next($request);
},
];
}
Resource Controller
ResourceController
Podemos pensar en los controladores como clases que menejan un recurso (un modelo) por lo que a la hora de crear el controlador podemos indicar la opción --resource que nos creara todos los métodos típicos para el manejo del recurso (CRUD)
php artisan make:controller PhotoController --resource
Luego si el controlador cuenta con todos los métodos de un recurso podemos crear la ruta de esta forma
use App\Http\Controllers\PhotoController;
Route::resource('photos', PhotoController::class);
Incluso puedes registrar multiples recursos al mismo tiempo de esta forma
Route::resources([
'photos' => PhotoController::class,
'posts' => PostController::class,
]);
Estas son los métodos que deberá tener el controlador para ser considera un recurso y poder crear la ruta de esta forma
index, create, store, show, edit, update, destroy
Estos nombres son el estándar utiliza para cada tipo de operación, más info en Actions Handled by Resource Controllers
Customizing Missing Model Behavior
Normalmente cuando un usuario no es encontrado se devuelve un 404, de todas formas se puede modificar este comportamiento definiendo el método missing cuando defines tu recurso, este método acepta una closure.
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
Normalmente los modelos que son soft deleted no serán recuperados, para recuperar también los modelos eliminados usar withTrashed
use App\Http\Controllers\PhotoController;
Route::resource('photos', PhotoController::class)->withTrashed();
Esto permitirá recuperar los modelos borrados en show, edit, update . Puedes especificar en los modelos en los que se pueden recuperar pasando un array con el nombre de los métodos.
Route::resource('photos', PhotoController::class)->withTrashed(['show']);
Specifying the Resource Model
Si usas route model binding (inyectar modelos enteros en el controlador), puedes tener el modelo al crear el controlador usando:
php artisan make:controller PhotoController --model=Photo --resource
Generating Form Requests
Con la opción --requests te genera un form request para el controlador
php artisan make:controller PhotoController --model=Photo --resource --requests
Partial Resource Routes
Puedes tener un controlador parcial, no es requerido que estén todos los métodos
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
Son las rutas que tienen todos los métodos propios de una api, todos los del resource excepto el create, edit
use App\Http\Controllers\PhotoController;
Route::apiResource('photos', PhotoController::class);
Puedes registrar múltiples rutas
use App\Http\Controllers\PhotoController;
use App\Http\Controllers\PostController;
Route::apiResources([
'photos' => PhotoController::class,
'posts' => PostController::class,
]);
Para generar un controlador con solamente index, show, update, store, destroy usar:
php artisan make:controller PhotoController --api
Nested Resources
Puedes definir recursos que están dentro de otros recursos, como por ejemplo, el modelo Fotos que tiene Comentarios. Puedes definir rápidamente rutas que con modelos anidados (padre-hijo)
use App\Http\Controllers\PhotoCommentController;
Route::resource('photos.comments', PhotoCommentController::class);
Esto genera lo siguiente:
/photos/{photo}/comments/{comment}
Scoping Nested Resources
Laravel es capaz de vincular los modelos anidados de forma que confirma que el modelo hijo pertenezca al modelo padre. Usando el método scoped cuando defines recursos anidados, puedes activar el scope automático así como indicar a laravel el campo por el que tiene que resolver el recurso hijo.
use App\Http\Controllers\PhotoCommentController;
Route::resource('photos.comments', PhotoCommentController::class)->scoped([
'comment' => 'slug',
]);
Esto registrara esta ruta anidada:
/photos/{photo}/comments/{comment:slug}
Para que esto funcione el modelo Photo deberá tener una relación llamada comments (el plural del parámetro pasado a la ruta), esta es la convención que usa laravel para obtener el modelo hijo
Supongamos que tienes una URL así: /photos/1/comments/my-comment-slug. Laravel hará lo siguiente:
- Buscará la foto con
id = 1. - Luego, buscará un comentario cuyo
slugseamy-comment-slugy confirmará que ese comentario esté asociado a la foto conid = 1.
Sin scoped, Laravel usaría el id del comment (en lugar del slug) y no verificaría automáticamente que el comment pertenezca a la photo en cuestión.
Localizing Resource URIs
Por defecto Route:resource creará las URIS usando los verbos en ingles y reglas en plural. Si necesitas localizar los verbos create, edit , puedes usar Route:resourceVerbs . Esto deberá realizarse en el método boot del App\Providers\AppServiceProvider
/**
* Bootstrap any application services.
*/
public function boot(): void
{
Route::resourceVerbs([
'create' => 'crear',
'edit' => 'editar',
]);
}
El laravel pluralizer soporta diferentes lenguajes el cual puedes configurar a tu gusto. Una vez que los verbos y la pluralización está definida una ruta como la siguiente: Route::resource('publicacion', PublicacionController::class) generará**:#**
/publicacion/crear
/publicacion/{publicaciones}/editar
Supplementing Resource Controllers
Si necesitas añadir rutas adiciones a un controlador más alla del set por defecto, Deberás definir estas rutas antes de la llamada a Route:resource . De lo contrarío las rutas definidas con resource pueden interferir con las rutas creadas.
use App\Http\Controller\PhotoController;
Route::get('/photos/popular', [PhotoController::class, 'popular']);
// Si resource estuviera antes, llamar a la uri /photos/popular llamaría a
// /photos/{photo} pasando popular como {photo}
Route::resource('photos', PhotoController::class);
ℹ️ Si frecuentemente necesitas métodos en el controlador que no son los por defecto, considera crear dos controladores pequeñas para estas rutas.
Singleton Resource Controllers
Hay ocasiones en las que para un modelo solamente puede haber un registro, como por ejemplo el profile de un usuario, un usuario no debería poder tener más de un profile.
En estos escenarios puedes crear una ruta con el método singleton , esto espera que el controlador solamente tenga los métodos edit, show, update , es decir, todos aquellos métodos que no son creacionales.
Route::singleton('photos.thumbnail', ThumbnailController::class);
Los recursos singleton también puede estar anidados:
Route::singleton('photos.thumbnail', ThumbnailController::class);
En este ejemplo. photos sería una ruta estándar, mientras que, thumbnail sería un recurso con las rutas mencionadas anteriormente.
Creatable Singleton Resources
En ocasiones quieres que se generen también los metodos de creación, lo puedes conseguir usando:
Route::singleton('photos.thumbnail', ThumbnailController::class)->creatable();
Esto generará todos los metodos de un recurso.
Si aparte de los 3 por defecto, solamente necesitas el destroy puedes usar:
Route::singleton(...)->destroyable();
API Singleton Resources
Route::apiSingleton('profile', ProfileController::class);
// show, update, destroy
Route::apiSingleton('photos.thumbnail', ProfileController::class)->creatable();
// show, store, update, destroy
Dependency Injection and Controllers
El contener de servicios de laravel resuelve todos los controladores, como resultado, puedes indicar cualquier clase de dependencia en el constructor del constructor y laravel se encargará de inyectarlo.
<?php
namespace App\Http\Controllers;
use App\Repositories\UserRepository;
class UserController extends Controller
{
/**
* Create a new controller instance.
*/
public function __construct(
protected UserRepository $users,
) {}
}
Method Injection
También puedes indicar dependencias en los métodos del controlador
<?php
namespace App\Http\Controllers;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
class UserController extends Controller
{
/**
* Store a new user.
*/
public function store(Request $request): RedirectResponse
{
$name = $request->name;
// Store the user...
return redirect('/users');
}
}
Los parametros de las rutas, también son inyectados en el método del controlador
use App\Http\Controllers\UserController;
Route::put('/user/{id}', [UserController::class, 'update']);
En este ejemplo se inyecta la Request y también el id del usuario
<?php
namespace App\Http\Controllers;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
class UserController extends Controller
{
/**
* Update the given user.
*/
public function update(Request $request, string $id): RedirectResponse
{
// Update the user...
return redirect('/users');
}
}