Controladores, middleware y formularios

icon picker
Controladores

Los controladores son una parte esencial del framework Laravel, con un controlador podemos agregarle dinamismo a nuestra aplicación ya que nos permite interactuar con modelos y otras clases de nuestra aplicación. En este artículo vamos a resaltar 10 características que no deberíamos olvidar.

Controlador de Recurso (Resource Controller)

Con Laravel crear un controlador que tenga todos los métodos necesarios para un CRUD es muy simple, gracias a la consola interactiva se puede crear con este simple comando:
php artisan make:controller UserController --resource
Además de crear un controlador para un CRUD, se puede asociar el controlador a un modelo añadiendo la opción model al comando anterior de esta forma:
php artisan make:controller UserController --resource --model=User
Para enlazar a estos controladores con todos sus métodos podemos hacerlo de forma simple en nuestras rutas sin tener que definir cada una, para hacerlo debemos usar el método resource:
Route::resource('users', 'UserController');
En el caso de no utilizar algún método en específico, se puede desactivar utilizando el método except y en el caso de que solo queramos utilizar los que especifiquemos, se puede hacer con el método only como se muestra a continuación:
Route::resource('users', 'UserController')->only([
'index', 'show', 'destroy']);

Route::resource('Users', 'UserController')->except (['create', 'store']);

Controladores de un solo método

Si tienes tiempo desarrollando con Laravel te has encontrado con controladores que cumplen una función muy específica, tanto que solo disponen de un método. Para este tipo de ocasiones, Laravel nos permite utilizar el método mágico __invoke de esta forma:
<?php
namespace App\Http\Controllers;
use App\Ticket;
use App\Http\Controllers\Controller;
class ShowTicket extends Controller
{
public function __invoke(Ticket $ticket)
{
return view('tickets.show', compact('ticket'));
}
}
Después de ver esto quizás te preguntes cómo podemos especificar el llamado de este método desde nuestras rutas. Pues, el método __invoke será lanzado al intentar llamar a la clase de un controlador sin especificar la acción:
Route::get('ticket/{ticket}', 'ShowTicket');

Métodos para el retorno de redirecciones

Existen algunos helpers muy útiles que pueden ayudarnos con las respuestas, en el caso de las redirecciones tenemos el helper back() el cual retorna una redirección hacia la ruta anterior y lo podemos usar de esta forma tan simple:
public function store(Request $request)
{
User::create($request->all());
return back();
}
image.png
Si quieres hacer una simple redirección hacia una dirección específica solo basta con utilizar el helper redirect():
public function store(Request $request)
{
User::create($request->all());
return redirect('/home');
}
Aunque si no te convence especificar rutas estáticas puedes concatenar el método route() y especificar el nombre de una ruta:
public function store(Request $request)
{
User::create($request->all());
return redirect()->route('users.index');
}
Podemos redireccionar hacia un dominio externo con el método away() de la siguiente forma:
public function foo()
{
return redirect()->away('https://www.sitio.net');
}

Redirecciones con mensajes de sesión

Los mensajes en la gran mayoría de los casos son muy importantes ya que le indicamos al usuario que está sucediendo; en Laravel podemos redirigir con algún tipo de mensaje desde el método de un controlador y es tan simple como esto:
public function destroy(User $user)
{
$user->delete();
return back()->with('success', 'Usuario eliminado con éxito.');
}
image.png
Con el método with podemos crear un mensaje de sesión temporal o «flash» si indicamos su nombre y su valor, además podemos mostrarlo en la vista de la siguiente forma:
@if (session('success')) // Comprobamos que exista
{{ session('success') }} // Imprimir el contenido indicando el nombre.
@endif
image.png
Si especificar el nombre y el valor del mensaje de sesión te parece mucho, existe una forma aun más simple siguiendo esta convención withSuccess el texto remarcado es el nombre del mensaje y como parámetro le pasaríamos el valor, como se puede ver en el siguiente ejemplo:
return back()->withSuccess('Valor del success');

{{ session('success') }} // Valor del success

return back()->withSitio('Sitio.net');
{{ session('sitio') }} // Sitio.net
image.png

Respuestas hacia otros métodos de los controladores

Cuando tenemos varios métodos que se repiten existe la posibilidad de responder hacia otro método. Un ejemplo puede ser éste:
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
class UserController extends Controller
{
public function store(Request $request)
{
$user = User::create($request->all());
return view('users.show', compact('user'));
}
public function show(User $user)
{
return view('users.show', compact('user'));
}
}

image.png
Seguramente ya lo has notado, en el método store luego de guardar el nuevo usuario se repite lo que hace el método show y aunque pueda parecer muy obvio, este caso se puede encontrar muy seguido y no es recomendable. Debido a que si en un futuro necesitas agregar algo de lógica a la vista tendrás que modificar dos métodos, además podrás tener problemas por envío duplicado de datos. La solución para evitar tener que reescribir código es redirigir hacía el método del controlador utilizando el método redirectToAction después del helper response de esta forma:
public function store(Request $request)
{
$user = User::create($request->all());
return response()-> redirectToAction ('UserController@show', $user);
}
image.png
El método acepta 4 argumentos, donde obligatoriamente debemos especificarle el controlador y la acción, de forma opcional podemos indicar los parámetros del método que estamos llamando, status y header.

Otros tipos de respuestas

Para responder una vista lo podemos hacer usando el helper view de esta forma:
public function index()
{
$variable = 'valor';
return view('users.index', compact('variable'));
}
image.png
El helper view recibe el nombre de la vista, en caso de estar dentro de carpetas debemos especificarlas y usar los puntos, cabe resaltar que no hace falta especificar la extensión de la vista ya que Laravel se encarga de esto. Como segundo argumento recibe un arreglo asociativo donde las llaves serán los nombres de las variables en la vista y los valores serán el contenido de la variable.
La función compact de PHP recibe los nombres de las variables a transformar en un arreglo asociativo, para que quede más claro podemos observar este código:
$variable = 'valor';
$temp = compact('variable');
// El contenido de temp es:
[
'variable' => 'valor'
]
image.png
Si estás trabajando sobre un controlador específico para un API puedes tener respuestas de tipo JSON de esta forma:
return response()->json([
'name' => 'Foo Bar',
'website' => 'sitio.net'
]);
image.png
Se puede resaltar que, por defecto, el método json configurará automáticamente el encabezado Content-Type a application/json.
Podemos responder descargas directas de archivos con el método download y es muy sencillo:
// Especificamos el path del archivo, el primer argumento es obligatorio, el resto es opcional.

return response()->download($direccionDelArchivo, $nombre, $headers);

// Podemos llamar el método deleteFileAfterSend
// si queremos que sea eliminado el archivo luego de responder.

return response()->download($direccionDelArchivo)->deleteFileAfterSend(true);
image.png
Con los métodos anteriores estamos retornando una descarga, pero si quieres una visualización de un archivo como puede ser un PDF o un archivo de texto en el navegador podemos hacerlo con el método file como se muestra a continuación:
// El primer parámetro es obligatorio y el segundo es opcional.
return response()->file($direccionDelArchivo, $headers);
image.png

Métodos authorize para los permisos

Con los controladores podemos comprobar los permisos que tenemos sobre algún recurso, para esto tenemos algunos métodos disponibles:
public function update(Request $request, Post $post)
{
// Con este método especificamos la acción y el recurso.
$this->authorize('update', $post);

// Podemos especificar un usuario con este método
$this->authorizeForUser($user, 'update', $post);
}
image.png
Laravel tomará el nombre de la acción y la asociará al recurso que hemos especificado, por ejemplo si definimos un método habitual como update en la clase de nuestro controlador estaremos comprobando que el usuario autentificado pueda hacer update sobre el modelo Post.

Validaciones

Desde los controladores tenemos la posibilidad de validar que estamos recibiendo lo que esperamos, existen algunas formas de cómo podemos hacerlo y vamos a comenzar observando la primera:
// Método store de un controlador
public function store(Request $request)
{
$this->validate($request, [
'name' => 'string|min:30|max:60',
'email' => 'unique:users,email|required'
], [
'name.*' => 'Nombre invalido',
'email.required' => 'El correo electronico es un campo obligatorio'
]);
}
image.png
El método validate recibe 3 argumentos: el objeto de una instancia Request, las reglas de validación y los mensajes si queremos personalizarlos, en el ejemplo se muestra cómo se puede personalizar el mensaje completo de un campo o específicamente sobre una regla.
Tenemos otra forma de hacerlo que puede ser más simple, ya que podemos llamar al método validate desde el objeto Request de la siguiente forma:
// Método store de un controlador
public function store(Request $request)
{
$request->validate([
'name' => 'string|min:30|max:60',
'email' => 'unique:users,email|required'
], [
'name' => 'Nombre invalido',
'email.required' => 'El correo electronico es un campo obligatorio'
]);
}
image.png
Este método solo recibe dos argumentos: las reglas de validación y como argumento opcional los mensajes personalizados.

Controladores para grupos de rutas

Ahora puede usar el método controller para definir un controlador común para todas las rutas dentro del grupo. Luego, al definir las rutas, solo necesitas proporcionar el Controller que invocan:
use App\Http\Controllers\OrderController;
Route::controller(OrderController::class)->group(function () {
Route::get('/orders/{id}', 'show');
Route::post('/orders', 'store');
});
image.png

Mejoras de validación para datos en matrices anidadas con version 9 laravel

A veces, es posible que necesite acceder al valor de un elemento de matriz anidado determinado al asignar reglas de validación al atributo. Ahora puede lograr esto usando el método Rule::forEach. El método forEach acepta una función de clausura que se invocará para cada iteración del atributo de matriz siendo validado, y recibirá el valor del atributo y el nombre de atributo explícito y completamente expandido. La función de clausura debe devolver una matriz de reglas para asignar al elemento de la matriz:
use App\Rules\HasPermission;
use Illuminate\Support\Facades\Validator;
use Illuminate\Validation\Rule;
$validator = Validator::make($request->all(), [
'companies.*.id' => Rule::forEach(function ($value, $attribute) {
return [
Rule::exists(Company::class, 'id'),
Want to print your doc?
This is not the way.
Try clicking the ⋯ next to your doc name or using a keyboard shortcut (
CtrlP
) instead.