Form request validation con mensajes personalizados

Como ya habíamos visto anteriormente en la entrada de cómo validar formularios con PHP, la validación es una parte importante en la recogida de datos para que estos no sean susceptibles de errores o no sean los esperados.

Esta entrada está enfocada a lo mismo pero desde Laravel. A partir de la versión 5 de Laravel, se implementa una clase muy útil que nos facilitará la validación de formularios.

Y para ello veremos 3 métodos muy sencillos que los diferencia en el volumen de validación al que lo queremos destinar. Pero antes…

Introducción

Laravel proporciona varias formas de validar los datos recogidos por la aplicación. Para ello se nutre de una clase base controladora llamada ValidatesRequests de la que podemos dar uso de un montón de reglas que validen las respuestas HTTP.

Preparando el entorno

Se va a suponer que vamos a montar un sistema de controlador para el manejo de funciones que procesan los datos y rutas+vistas que nos lancen los formularios de recogida de datos.

// rutas para mostrar y procesar el formulario
Route::get('personajes/create', 'PersonajesController@create');
Route::post('personajes/store', 'PersonajesController@store');
// clase con métodos para el manejo del modelo Personaje
class PersonajesController extends Controller
{
    public function create()
    {
        return view('personajes.create');
    }


    public function store(Request $request)
    {
        /* recogida - validación - guardado */
    }
}

Método 1.- Validator::make

Este método se puede dividir en 3 sencillos pasos: recogida, reglasvalidación.

Lo primero de todo es la recogida de los request input del formulario de la vista. Seguidamente generamos una serie de reglas que entenderá ValidatesResquests para restringirle una norma y para finalizar, pasar por validación los datos y las reglas a la clase Validator mediante el método make():

// @vars Request $request
public function store(Request $request)
{
  $data = Request::all();

  $rules = [
    'nombre' => 'required|max:191',
    'descripcion => 'required|max:255',
    'nivel' => 'required|integer|min:0|max:10
  ];

  $validation = Validator::make($data, $rules);

  if( $validation->fails() )
  {
    return redirect()->back()
      ->withErrors( $validator->errors() )
      ->withInput();
  }

  $personaje = Personaje::create($request->all());
  return redirect('personajes/create')->with('status', 'Personaje creado OK');
}
  • La variable $data almacena toda la información recogida en los inputs del formulario.
  • El array $rules contiene un conjunto campo/reglas asociativo donde se detalla la regla que debe seguir cada campo del formulario. En este caso, todos los campos son requeridos, ‘nivel’ tiene que ser un número entero entre 0 y 10, límites de caracteres para los campos nombre y descripción… Para un mayor detalle de la clase de reglas que se pueden pasar al validador, aquí está en enlace.
  • El objeto $validation contiene la validación al que se le pasa la información y las reglas.
  • Si su método fails() es afirmativo, le ordenamos que vuelva al formulario con los errores del validador y el contenido que tenían los campos. Si el validador no falla, grabamos el nuevo registro y retornamos con un mensaje afirmativo.

Método 2.- validate()

Como ya se había mencionado al principio de la entrada, con el lanzamiento de Laravel 5, se ha implementado el método validate() el cual nos facilita de sobremanera la validación.

Con las mismas reglas que teníamos configuradas anteriormente hacemos la llamada al método validate() que recibe como parámetros: la variable $request y las reglas.

Este método interrumpirá inmediatamente el proceso de guardado si encuentra algún fallo, devolviéndolos a la vista:

// @vars Request $request
public function store(Request $request)
{
  $rules = [
    'nombre' => 'required|max:191',
    'descripcion => 'required|max:255',
    'nivel' => 'required|integer|min:0|max:10
  ];

  $this->validate($request, $rules);

  $personaje = Personaje::create($request->all());
  return redirect('personajes/create')->with('status', 'Personaje creado OK');
}

Método 3.- Form Request

Para la creación de un customizable Form Request, nos valdremos del comando del cliente de artisan make:request. Al ejecutar este comando, inmediatamente se nos crea (de no existir) un directorio en app/Http/Requests/[nombredelaclase.php]

/*
Ejemplo de comando
por convenio se terminaría con Request
al final de la clase
*/

php artisan make:request CreatePersonajeRequest

La clase resultante está compuesta básicamente por dos métodos: authorize rules.

Para que no haya problema de credenciales y que todo usuario pueda hacer uso del formulario se habilita a verdadero el método authorize.

// Determina si el usuario está autorizado para enviar el request
public function authorize()
{
  return true;
}

En rules retornaremos las mismas reglas que teníamos en los métodos anteriores.

// Se aplican las reglas de validación al request
public function rules()
{
  return [
    'nombre' => 'required|max:191',
    'descripcion' => 'required|max:255',
    'nivel' => 'integer|required|min:0|max:10'
  ];
}

Y ahora en nuestro controlador debemos enviarle un objeto de esta clase que acabamos de crear

// @vars $request
public function store(CreatePersonajeRequest $request)
{
  $personaje = Personaje::create($request->all());
  return redirect('personajes/create')->with('status', 'Personaje creado OK');
}
  • Como detalle, en el controlador debemos agregar la ruta de la clase en la cabecera:
    use App\Http\Requests\CreatePersonajeRequest;

    Devolver errores personalizados

Si seguimos con el último método de Form Request podremos persnalizar los mensajes de error de Validate. Ya que por defecto, Laravel utiliza un conjunto de cadenas a las que tendríamos que traducir en la carpeta lang

Simplemente, en la clase CreatePersonajeRequest creada anteriormente sobrescribimos los mensajes con la función messages(). Esta función devolverá un array de pares de cadenas asociativas para cada uno de los ‘campo.regla’:

// Devuelve un mensaje por cada atributo erróneo
public function messages()
{
  return [
    'nombre.required' => 'Olvidaste el nombre del personaje',
    'descripcion.required' => 'Describe brevemente el personaje',
    'descripcion.max' => 'Te has pasado de caracteres, solo 255',
    'nivel.min' => 'El nivel como mínimo debe tener 0',
    'nivel.max' => 'El nivel como máximo debe tener 10',
    'nivel.integer' => 'El nivel debe ser una cifra entre 0 y 10'
  ];
}

Pero, ¿cómo vemos esto reflejado en la vista? Para ello nos vamos a basar en el formulario de registro que ya usa Laravel para los usuarios (si hemos implementado auth).

Validate() envía un conjunto de errores (de tenerlos) a la vista, los cuales podemos recorrer y generar un listado de errores con @foreach o segregarlos por cada uno de los campos que le corresponda. Esta última forma me gusta más.

Por ejemplo, en el campo del nombre, habrá que imprimir el error que corresponda obviamente a ‘nombre’, accediendo a su primer error encontrado:

<input type="text" name="nombre" value="{{ old('nombre') }}" required>

@if ($errors->has('nombre')) 
  {{ $errors->first('nombre') }}
@endif

O bien, si queremos embellecerlo con BootStrap:

<input type="text" class="form-control{{ $errors->has('nombre') ? ' is-invalid' : '' }}" name="nombre" value="{{ old('nombre') }}" required>

@if ($errors->has('nombre'))
  <span class="invalid-feedback">
    <strong>{{ $errors->first('nombre') }}</strong>
  </span>
@endif

De esta manera, habremos conseguido validar los datos enviados por formulario gracias a Form request validation, a los que también le hemos personalizado los mensajes de error.

3 comentarios en “Form request validation con mensajes personalizados”

  1. Hola:

    Suponiendo el caso de un UserController cuyo modelo tenga un campo de USERNAME que tenga que ser REQUIRED y UNIQUE para cada usuario.
    Entonces, en el método STORE, la regla de validación es:

    ‘username’ => ‘required|string|max:69|unique:users’,

    La cosa es que, en la acción de UPDATE, la regla deberá partir de que el USERNAME siga siendo UNIQUE pero sin tener en cuenta el propio de ese usuario que se está modificando; luego la regla es así:

    ‘username’ => ‘required|string|max:255|unique:users,username,’ . $request->id

    Como se puede deducir el » $request->id » llega al método UPDATE del UserController.

    public function store(UserUpdateRequest $request) { … }

    La cuestión es que la variable se recibe en el UserController@update.
    La duda que tengo es si llega a estar accesible desde dentro de la clase UserUpdateRequest y es posible llamarla en la regla como he expuesto o, sino, ¿cómo debería acceder a ella para comprobar la regla tal como está definida?

    Responder
    • Hola Pedro.

      Buena observación la tuya. En el caso de encontrarnos con una actualización de usuario único la regla debería ser exclusiva de la actualización.

      Lo que yo haría sería guiarme por el método 3 de Form Request. En el que nosotros podemos definir las clases que queramos y haría una para la inserción de nuevos datos y otra para la actualización de la misma.

      Laravel conocerá (mediante las rutas) si vamos a hacer un POST o un PUT. Puedes ayudarte de ello tal y como aparece en este ejemplo que he encontrado con el mismo caso. https://laracasts.com/discuss/channels/laravel/form-request-updating-a-unique-field

      Responder

Deja un comentario