La validación es una cuestión fundamental en cualquier aplicación, no sólo web. Cualquiera.
Validar consiste en verificar la validez de los datos antes de procesarlos.
¿Validar en cliente o en servidor?.
En el cliente es más rápido, no necesitamos conectar con el servidor ni hacer uso de la red.
Pero nunca debemos confiar en la validación realizada en el cliente. Puede ser burlada intencionadamente o anulada de forma involuntaria.
Validar en el cliente es opcional pero en el servidor es obligatorio.
Cómo validar en Laravel
Existen varias maneras de hacerlo.
Nosotros usaremos el método validate del objeto $request.
Ejemplo:
public function store(Request $request)
{
$request->validate([
'title' => 'required|unique:posts|max:255',
'body' => 'required',
]);
//validado -> a guardar
}
¿¿Qué pasa tras la validación ??
Si la validación falla se produce una excepción y se reenvía la petición a la URL previa.
Se crea un objeto $errors que permite mostrar los errores de validación.
Los datos del formulario se flashean a la sesión y estarán disponibles sólo en la próxima peticicón para ser volver a rellenar el formulario como estaba.
OJO. Si usamos Ajax se envía una respuesta al cliente con un código 422 y un JSON con los mensajes de error.
Rellenando con los datos viejos
La función helper old() nos permite acceder a los datos que no se han validado y están en la sesión.
Hemos planteado la forma habitual pero existen otras:
Creando clases Request a la medida de una validación.
Creando clases Validator
Ambas soluciones son algo más sofisticadas y no las vamos a usar aunque podrían dar solución a situaciones más complejas.
Autorización: reglas y políticas
Laravel nos brinda dos sistemas para autorizar acciones: reglas y puertas.
Reglas
Las reglas se definen asociadas a la clase Gate.
Son una solución más simple.
Políticas
Son soluciones más elaboradas que engloban la autorización referida a un modelo concreto.
Se definen en clases dentro guardadas en app/Policies
Tienen una analogía a rutas y controladores, simplicidad vs complejidad y orden.
En un proyecto usaremos una u otra (o ambas) de acuerdo a su complejidad y envergadura.
Definir reglas
Las reglas se definen en el AuthServiceProvider.php, dentro de su función boot.
Podemos definir una regla con un closure (función anónima) dentro de dicho método:
public function boot()
{
$this->registerPolicies();
//nuestra regla es esta:
Gate::define('update-post', function ($user, $post) {
return $user->id == $post->user_id;
});
}
Usar reglas
Para comprobar las reglas debemos usar la fachada Gate y alguno de sus métodos (allows, denies). Lo podemos hacer en la propia ruta o más correctamente en el controlador o en un middleware:
if (Gate::denies('update-post', $post)) {
abort(403);
//o podríamos redireccionar:
// return redirect('/home');
// o mostrar una vista
// return view('nombreVista');
}
Observa que el $user no es pasado a la clase Gate, toma el autenticado. Si quisiéramos pasarlo debemos hacerlo así:
//preguntando si se le permite
if (Gate::forUser($user)->allows('update-post', $post)) {
// The user can update the post...
}
//o al revés ...
if (Gate::forUser($user)->denies('update-post', $post)) {
// The user can't update the post...
}
Políticas
Si el proyecto es pequeño la solución anterior es válida pero para algo grande puede no ser una buena solución.
Las políticas separan código para no sobrecargar el código del AuthServiceProvider.
Las políticas son clases guardadas en app/Policies y que se crean así:
//clase en blanco
php artisan make:policy PostPolicy
//clase con reglas CRUD predefinidas asociada a un modelo
//recomendable:
php artisan make:policy PostPolicy --model=Post
Tras crear las políticas, éstas deben ser regitradas en el AuthServiceProvider:
class AuthServiceProvider extends ServiceProvider
{
//deben incluírsee en el siguiiente array:
protected $policies = [
Post::class => PostPolicy::class,
];
Escribiendo las políticas:
Podemos crear los métodos que necesitemos. Por ejemplo el método update lo podemos usar para actualizar un objeto del modelo afectado. Sus argumentos deben ser el user y un objeto del modelo protegido:
public function update(User $user, Post $post)
{
return $user->id === $post->user_id;
}
//aunque si el método no usa ningún objeto del modelo lo podemos dejar así:
public function create(User $user)
{
return true; //una expresión booleana...
}
El método before permite tomar decisiones generales sin evaluar la política
public function before($user, $ability)
{
if ($user->isSuperAdmin()) {
return true;
}
}
// si devuelve true -> autorizado. No comprueba nada más
// si devuelve false -> denegado. No comprueba nada más