Laravel, parte 9: Validaciones

En la publicación pasada aprendimos cómo crear un simple módulo de CRUD, para administrar nuestros usuarios en la base de datos. Sin embargo, en aquella ocasión no habíamos tenido algo en cuenta muy importante: las validaciones.

En mis años de desarrollador web las validaciones siempre me parecieron algo bastante denso, pero obviamente necesario. Las validaciones normalmente llevan mucho tiempo, dependiendo de la cantidad de campos de formulario y reglas que tengamos en estos. Pero las validaciones siempre deben estar, para evitar que por ejemplo se guarden datos vacíos o corruptos en nuestra base de datos.

Puede haber dos sistemas de validaciones en nuestra aplicación, las del cliente y las del servidor. Las validaciones del lado del cliente son las que se crean con Javascript, mientras que las del servidor son las que se realizan con lenguajes como PHP, Ruby, Python, Java, o cualquier otro lenguaje del servidor con la que se haya creado nuestra aplicación web.

Las validaciones del lado del cliente, sirven para evitar el envío de información incorrecta al servidor, y así dejar estos problemas resolviéndose en el navegador del usuario, el problema es que un usuario con conocimientos de internet puede burlar fácilmente esta seguridad y enviar datos innecesarios al servidor. Por tanto el servidor debe tener su propia barrera de seguridad, para así evitar la toma de datos incorrectos.

Si querés saber sobre validaciones del lado del navegador, podés revisar estas publicación que hice hace un tiempo: Validar formularios con html5 y Validar formularios con Jquery. Pero en esta ocasión veremos cómo hacerlo desde el servidor y con Laravel.

Como dije hace un tiempo, y lo sigo sosteniendo, Laravel permite acelerar bastante el trabajo que lleva mucho tiempo al crear una aplicación web. Las validaciones no son la excepción.

Gracias a la clase Validator y el método make(), ya podés realizar una competa validación. Este método recibirá dos arrays asociativos, uno con los valores a validar y otro con las reglas que deberían respetar esos valores:

Validator::make(array(
   'name' => 'Pepe Argento'
), array(
   'name' => 'required|min:2'
));

En este caso vamos a evaluar que el valor ‘name’ que es igual a ‘Pepe Argento’, cumple la siguiente regla: sea obligatorio y tenga como mínimo dos caracteres.

Para ello vamos a ir a nuestro modelo User, en app -> models -> User.php, vamos a editar este archivo con el siguiente código:

<?php
class User extends Eloquent { 
   public static $rules = array(
      'real_name' => 'required|min:2',
      'email' => 'required|email|unique:users,email',
      'password' => 'required',
      'level' => 'required|numeric'
   );
   public static function validate($data){
      $reglas = self::$rules;
      return Validator::make($data, $reglas);
   }
}
?>

En primer lugar definimos nuestra reglas de validación con un array:

public static $rules = array(
   'real_name' => 'required|min:2',
   'email' => 'required|email|unique:users,email',
   'password' => 'required',
   'level' => 'required|numeric'
);

Las propiedades ‘real_name’, ‘email’, ‘password’ y ‘level’ deben ser obligatorias. Además ‘real_name’ debe tener como mínimo dos caracteres. ‘email’ debe ser un formato válido para los correos electrónicos, y debe ser único, osea que se intenta ingresa un email que le pertenezca a otro usuario, debería devolvernos un error. Y finalmente ‘level’ debe ser numérico.

Con respecto al método validate(), que acabamos de crear, recibirá como parámetro un array con los valores, y hará una comparación con las reglas, devolviendo así el resultado de la validación con el método make(), de la clase Validator.

Ahora vamos a volver a nuestro controlador UsersController.php, en app -> controllers -> UsersController.php, y editaremos el método store() con el siguiente código:

public function store() {
   $user = new User();
   $user->real_name = Input::get('real_name');
   $user->email = Input::get('email');
   $user->password = Hash::make(Input::get('password'));
   $user->level = Input::get('level');
   $user->active = true;
   $validator = User::validate(array(
      'real_name' => Input::get('real_name'),
      'email' => Input::get('email'),
      'password' => Input::get('password'),
      'level' => Input::get('level'),
   ));
   if($validator->fails()){
      $errors = $validator->messages()->all();
      $user->password = null;
      return View::make('users.save')->with('user', $user)->with('errors', $errors);
   }else{
      $user->save();
      return Redirect::to('users')->with('notice', 'El usuario ha sido creado correctamente.');
   }
}

Antes de llamar al método save() para guardar el registro, comprobaremos que los datos enviados son correctos.

Primero le pasamos al método validate(), que creamos en el modelo User, un array asociativo con los valores enviados desde los campos de formulario:

$validator = User::validate(array(
   'real_name' => Input::get('real_name'),
   'email' => Input::get('email'),
   'password' => Input::get('password'),
   'level' => Input::get('level'),
));

Luego llamamos al método fails(), del objeto $validator, que nos devolverá true si hubo errores de validación:

$validator->fails()

De haber errores de validación, guardaremos estos errores en una variable de nombre $errors:

$errors = $validator->messages()->all();

Y cargaremos el formulario nuevamente, con el usuario y sus valores, y los errores de la carga:

return View::make('users.save')->with('user', $user)->with('errors', $errors);

Pero si no hubo errores guardaremos el registro con el método save().

Ahora vamos a ir a app -> views -> users -> save.blade.php y vamos a agregar nuestro a archivo, en alguna parte del código lo siguiente:

@if(isset($errors))
   <ul>
      @foreach($errors as $item)
         <li> {{ $item }} </li>
      @endforeach
   </ul>
@endif

Aquí comprobaremos si hay errores, y de haberlos mostramos el listado de los mismos.

Ahora, antes de continuar con la acción para modificar el usuario, vamos a detenernos un momento. Dentro de nuestras reglas de validación, una de ellas comprobará que al guardar el usuario no exista ese email que se ha enviado. Sin embargo, cuando se está intentando modificar el usuario, si ingresamos en el campo de formulario el mismo email que tiene ese usuario actualmente, se tomará como erróneo, porque claro, ese email existe en la base de datos, aunque sea del email del usuario, por tanto debería estar validado.

Para solucionar este problema, debemos cambiar nuestra regla, primero comprobar que el email no le pertenezca al mismo usuario que se intenta modificar. Así que vamos a modificar nuestro modelo User de la siguiente forma:

<?php
class User extends Eloquent { 
   public static $rules = array(
      'real_name' => 'required|min:2',
      'email' => 'required|email|unique:users,email,id',
      'password' => 'required',
      'level' => 'required|numeric'
   );
   public static function validate($data, $id=null){
      $reglas = self::$rules;
      $reglas['email'] = str_replace('id', $id, self::$rules['email']);
      return Validator::make($data, $reglas);
   }
}
?>

Bueno, como se ve en el código, cambiamos la regla, agregando el valor id:

'email' => 'required|email|unique:users,email,id',

Ahora, cuando llamamos al método validate(), debemos pasar como parámetro el id, que por defecto será null (por si se está intentando insertar), y reemplazaremos ese valor ‘id’, por el que se ha pasado como parámetro:

$reglas['email'] = str_replace('id', $id, self::$rules['email']);

De esta manera, la regla será la siguiente: el email no debe repetirse, a menos que obviamente pertenezca al usuario con ese id.

Así que vamos a volver a nuestro controlador UsersController, y vamos a modificar el método update() con el siguiente código:

public function update($id) {
   $user = User::find($id);
   $user->real_name = Input::get('real_name');
   $user->email = Input::get('email');
   $user->level = Input::get('level');
   $validator = User::validate(array(
      'real_name' => Input::get('real_name'),
      'email' => Input::get('email'),
      'password' => $user->password,
      'level' => Input::get('level'), 
   ), $user->id);
   if($validator->fails()){
      $errors = $validator->messages()->all();
      $user->password = null;
      return View::make('users.save')->with('user', $user)->with('errors', $errors);
   }else{
      $user->save();
      return Redirect::to('users')->with('notice', 'El usuario ha sido modificado correctamente.');
   }
 }

En primer lugar llamamos el método validate(), al que pasaremos el array con los valores a validar, y como segundo parámetro, el id del usuario a modificar.

$validator = User::validate(array(
   'real_name' => Input::get('real_name'),
   'email' => Input::get('email'),
   'password' => $user->password,
   'level' => Input::get('level'), 
), $user->id);

Y repetimos el mismo proceso que en la acción store(), dependiendo de si hay errores o no de validación.

Ahora, bien, si has probado la aplicación, habrás notado que al devolvernos los errores de validación, estos están escritos en inglés. Para personalizar nuestros mensajes de error tendremos que modificar nuestro modelo nuevamente de la siguiente formar

<?php
class User extends Eloquent { 
   public static $rules = array(
      'real_name' => 'required|min:2',
      'email' => 'required|email|unique:users,email,id',
      'password' => 'required',
      'level' => 'required|numeric'
   );
   public static $messages = array(
      'real_name.required' => 'El nombre es obligatorio.',
      'real_name.min' => 'El nombre debe contener al menos dos caracteres.',
      'email.required' => 'El email es obligatorio.',
      'email.email' => 'El email debe contener un formato válido.',
      'email.unique' => 'El email pertenece a otro usuario.',
      'password.required' => 'La contraseña es obligatoria.',
      'level.required' => 'El nivel es obligatorio.',
      'level.numeric' => 'El nivel debe ser numérico.'
   );
   public static function validate($data, $id=null){
      $reglas = self::$rules;
      $reglas['email'] = str_replace('id', $id, self::$rules['email']);
      $messages = self::$messages;
      return Validator::make($data, $reglas, $messages);
   }
}
?>

Como se ve en el ejemplo, creamos una variable estática llamada $messages:

public static $messages = array(
   'real_name.required' => 'El nombre es obligatorio.',
   'real_name.min' => 'El nombre debe contener al menos dos caracteres.',
   'email.required' => 'El email es obligatorio.',
   'email.email' => 'El email debe contener un formato válido.',
   'email.unique' => 'El email pertenece a otro usuario.',
   'password.required' => 'La contraseña es obligatoria.',
   'level.required' => 'El nivel es obligatorio.',
   'level.numeric' => 'El nivel debe ser numérico.'
);

Cada índice estará formado por el nombre del atributo más un punto y la regla, y el valor será justamente el mensaje de error, si la regla no se cumple:

Y cuando llamamos al método validate():

public static function validate($data, $id=null){
   $reglas = self::$rules;
   $reglas['email'] = str_replace('id', $id, self::$rules['email']);
   $messages = self::$messages;
   return Validator::make($data, $reglas, $messages);
}

Al llamar al método make(), agregaremos un tercer parámetro con los mensajes de validación.

De este modo tenemos nuestras validaciones listas, muy fáciles de usar y de mantener. Otra prueba de la facilidad que nos provee Laravel.

Descargar ejemplo

Anterior: Laravel, parte 8: CRUD

Siguiente: Laravel, parte 10: Login

Redes sociables

    8 Replies to “Laravel, parte 9: Validaciones”

    1. Fernando eh seguido tus tutoriales desde el principio, te felicito y te doy las gracias, son muy buenos y me han servido de gran ayuda..

      1. Gilberto, gracias a vos por comentar. Me alegro que te hayan servido, después seguiré subiendo más cosas.

        Seguimos en contacto. Que tengas un lindo día.

        Saludos!

    2. Hermano Fer, un saludo.

      Muy buen tutorial, te felicito. Aunque tengo una duda.

      En la parte anterior (CRUD) al hacer las vistas, en especifico la de save.blade.php hemos puesto como oculto el campo del password (en el caso de la opción “Edit”) y visible para cuando es un nuevo registro.

      Ahora bien, en este capitulo al hacer las validaciones me encontré con un problema. Al usar la validación para la opción “Edit” me envía el mensaje de error: “La contraseña es obligatoria” (dicho campo está oculto en esta vista). Así que me lo he montado a la fácil, he hecho dos bloques de reglas, idénticas salvo una linea. Para los nuevos registros pido el password y para los cambios no. Con esto se soluciona el problema.

      La pregunta es: ¿Hay alguna otra solución en la que no tenga que escribir dos veces ese bloque? Lo que yo hice funciona pero rompo con la idea de lo que es el CRUD, exceptuando dicha linea, estoy escribiendo dos veces el mismo bloque de código en el User.php ¿o en algún punto hice algo mal?

      1. Eso depende de la regla de negocio que tangas, en este caso la regla dice que la contraseña sólo se ingresa cuando se inserta y no cuando se modifica.

        Saludos!

    3. Gracias Fernando por participar tus conocimientos.

      Inquietud: Que es mejor utiliza, Blade o Angularjs?

      Nuevamente gracias

    Comments are closed.