En este nuevo tutorial veremos cómo validar un formulario y guardar datos con Laravel 4. En los capítulos anteriores ya vimos los siguientes temas:
- Creamos el controlador (resource controller) y le asignamos la ruta
- Creamos un layout para nuestro módulo, usando Bootstrap
- Creamos el formulario para nuestro módulo e hicimos una prueba de envío de datos
Validar los datos enviados en el formulario
Esta es una tarea tan común que todos los frameworks incluyen clases dispuestas a asistir al usuario en la validación de datos. Laravel por supuesto no es la excepción.
Volvamos a la función store donde quedamos anteriormente:
Código :
public function store() { return Input::all(); }
Acá haremos todo el proceso.
Primero que nada, vamos a crear un nuevo usuario (new User)
En Laravel los registros de la base de datos se pueden manejar como objetos gracias al ORM de Laravel: Eloquent.
Dado que, como les dije, cada registro (fila de la tabla users) es manejado como si fuera un nuevo objeto, y aquí intentamos crear un nuevo usuario, pues:
Código :
$user = new User
Esto funciona sólo porque Laravel viene con un modelo User.php ya predefinido en:
Código :
app/models/User.php
Ve a darle un vistazo y regresa cuando estés listo.
Ok, un gusto tenerte de vuelta.
Lo que haremos a continuación es interactuar con nuestro controlador Admin_UserController y nuestro modelo User para validar y guardar el usuario.
Recuerda que como vimos en otro tutorial, el controlador debe apoyarse en los modelos y otras clases lo más posible, de manera que la programación del controlador sea limpia y la “lógica de negocio” o dicho de otra forma, el “código pesado” esté en nuestro modelo.
Nosotros, en otras palabras, queremos que la acción store de nuestro controlador luzca así:
Código :
/** * Store a newly created resource in storage. * * @return Response */ public function store() { // Creamos un nuevo objeto para nuestro nuevo usuario $user = new User; // Obtenemos la data enviada por el usuario $data = Input::all(); // Revisamos si la data es válido if ($user->isValid($data)) { // Si la data es valida se la asignamos al usuario $user->fill($data); // Guardamos el usuario $user->save(); // Y Devolvemos una redirección a la acción show para mostrar el usuario return Redirect::route('admin.users.show', array($user->id)); } else { // En caso de error regresa a la acción create con los datos y los errores encontrados return Redirect::route('admin.users.create')->withInput()->withErrors($user->errors); } }
El código se explica solo, de todas formas:
- Creamos un nuevo usuario (new User).
- Tomamos los datos enviados por el usuario (Input::all()).
- Si son válidos se lo asignamos al usuario, guardamos el usuario y redireccionamos a “show”.
- Si no son válidos redireccionamos a la pantalla anterior pero llevándonos de regreso la data y los errores generados en la validación, para mostrárselos al usuario.
¿Qué nos hace falta acá?
Bueno, básicamente nunca le dijimos a Laravel qué o cómo validar, de hecho la función User::isValid() no existe, si presionamos el botón “Crear usuario” nos va a arrojar lo siguiente:
Código :
Call to undefined method Illuminate\Database\Query\Builder::isValid()
¿Ahora qué?
Vamos al modelo (app/models/User.php) y agregamos lo siguiente:
Código :
public $errors; public function isValid($data) { $rules = array( 'email' => 'required|email|unique:users'', 'full_name' => 'required|min:4|max:40', 'password' => 'required|min:8|confirmed' ); $validator = Validator::make($data, $rules); if ($validator->passes()) { return true; } $this->errors = $validator->errors(); return false; }
Primero definimos una propiedad dentro del modelo User que servirá para almacenar los errores (en caso de que haya alguno), la coloqué pública para simplificar el ejemplo:
Código :
public $errors;
Lo segundo es definir el método isValid, que acepta como parámetro la data enviada por el usuario. Dentro fíjense que defino un array con las reglas de validación “rules”.
Este array $rules es procesado por la clase de validación de Laravel (Validator) cuyo método Validator::make acepta, como primer parámetro un array de datos ($data) y como segundo parámetro un array de reglas ($rules).
Fíjense que las claves de ambos arrays coinciden y se refieren a los nombres de los campos (en nuestro ejemplo: email, full_name y password):
Entonces llamamos al método:
Código :
$validator = Validator::make($data, $rules)
Así se crea un nuevo objeto almacenado en $validator que contiene toda la data y las reglas de validación. Ahora todo lo que hace falta es ejecutar la validación:
Código :
$validator->passes() //devuelve TRUE si la validación pasa
Código :
$validator->fails() //Método pesimista, devuelve TRUE si la validación falla
Si la validación pasa, retornamos TRUE en nuestro método isValid, si falla entonces almacenamos en la propiedad $errors los errores para decirle al usuario qué salió mal:
Código :
$this->errors = $validator->errors();
Y retornamos FALSE.
Prácticamente siempre que necesitemos validar algo con Laravel, usaremos este método, lo que cambia son las reglas que tenemos que definir.
Como pueden ver en el ejemplo anterior, las reglas se definen escribiendo un array asociativo, donde la llave (key) es el nombre del campo, y el valor es las reglas asignadas a ese campo separadas con barra | y si la regla necesita un parámetro adicional se define con : y si son varios parámetros es separan por coma, así:
Código :
'nombre_de_campo' => 'regla1|regla2|regla_con_parametros:parametro1,parametro2'
Reglas de validación de Laravel
Según usamos en el ejemplo:
- required sirve para indicar que un campo es obligatorio.
- email indica que el campo es de tipo email.
- min:value exige que el campo tenga un mínimo de “value” caracteres.
- max al contrario del anterior, delimita el máximo posible.
- confirmed útil para confirmar email o contraseña, exige que haya un campo con el mismo nombre pero con el sufijo “_confirmation” y que ambos tengan el mismo valor (ej: password y password_confirmation).
- unique:table exige que el campo sea único en la tabla “table” (útil para campos como username e e-mail).
Si quieren ver todas las reglas de validación de Laravel vean la documentación oficial
Penúltimo paso:
Indicarle a Laravel qué campos se pueden llenar por “asignación masiva”
Volviendo al controlador, cuando hacemos:
Código :
$user->fill($data)
Estamos haciendo algo llamado en Laravel “Massive assignment” (asignación masiva), es decir llenar todas las propiedades del modelo a través de un array de datos. Esto puede traer varios problemas, por ello Laravel nos permite indicarle al modelo qué campos son “llenables” (fillable) a través de métodos como User::fill:
En el modelo (app/models/User.php) agreguen esta otra propiedad:
Código :
protected $fillable = array('email', 'full_name', 'password');
Ahora aunque algún aprendiz de hacker intente enviar campos adicionales no conseguirá romper la funcionalidad dado que sólo esos 3 campos son tomados en cuenta, más aún, tampoco se intentará grabar el campo oculto “_token” ni el campo “password_confirmation” que no hace falta en la tabla “users”.
Y ahora un último paso:
Si llenamos nuestro formulario de forma inválida, digamos, escribieron “pepito” en el campo email, la aplicación es redireccionada de vuelta al formulario (ruta admin.users.create) pero no se muestran los errores ni nada, esto está muy mal…
Cómo mostrar los errores de una validación fallida:
Laravel parece ser un poco pesimista, porque siempre, en nuestras vistas, se encuentra definida una variable llamada $errors con un objeto de tipo MessageBag que tiene algunos métodos bastante básicos, uno de ellos es any() que revisa si de verdad hay algún error, otro es all() que devuelve el array con todos los posibles errores.
Agreguemos esto a la vista app/views/admin/users/form.blade.php justo debajo del título:
Código :
@if ($errors->any()) <div class="alert alert-danger"> <button type="button" class="close" data-dismiss="alert">×</button> <strong>Por favor corrige los siguentes errores:</strong> <ul> @foreach ($errors->all() as $error) <li>{{ $error }}</li> @endforeach </ul> </div> @endif
Ahora intenten re-enviar el formulario nuevamente con el email “pepito” y verán una lista de mensajes en rojo diciéndote qué salió mal:

¡Excelente!
- Cambiar en app/config/app.php la variable “locale” a “es” (actualmente tiene en).
- Ir al directorio app/lang/ copiar la carpeta en/ y pegarla en el mismo directorio, renombrándola a es/
- Abrir la carpeta app/lang/es recién duplicada y traducir todos los mensajes a español.
Es posible que estos mensajes ya estén disponibles si buscan en Google, por ahora esto se escapa un poco del alcance de nuestro tutorial.
Mantener los valores especificados por el usuario
El problema con esto es que aunque ya estamos mostrando la data no estamos devolviendo los valores al usuario, aunque sea un error, el campo email debería decir “pepito” y no estar en blanco, de forma que el usuario pueda completar [email protected] sin tener que reescribir todo de nuevo, por ejemplo.
Esto se puede resolver de forma muy simple con Laravel:
Modifiquemos nuestro método create de manera que quede así:
Código :
public function create() { // Creamos un nuevo objeto User para ser usado por el helper Form::model $user = new User; return View::make('admin/users/form')->with('user', $user); }
Básicamente lo mismo que teníamos antes, sólo que instanciamos un nuevo objeto User.
En nuestro form app/views/admin/users/form.blade.php vamos a cambiar Form::open por otro helper llamado Form::model, que es muy parecido, sólo que como primer parámetro acepta un modelo, y lo demás queda igual:
Código :
{{-- Reemplazamos Form::open por Form::model: --}} {{ Form::model($user, array('route' => 'admin.users.store', 'method' => 'POST'), array('role' => 'form')) }}
¡Listo! Ahora prueben registrarse con email pepito por última vez, verán que no sólo se muestran los errores sino que Laravel ahora rebota los datos nuevamente al formulario.
¡Excelente!
Por último veamos un gráfico de lo que sucede entre el usuario y el servidor cuando el usuario pone “pepito” u otro email o campo no-válido:

Esto nos ilustra las diferentes redirecciones que se llevan a cabo de manera casi transparente.
Ahora si escriben todos los datos de forma válida, verán cómo Laravel los redirecciona a la vista “show” que por ahora sólo muestra algo así:
Código :
Aqui mostramos la info del usuario: 1
Y este sería el gráfico si todo va bien:

En próximos tutoriales por supuesto mejoraremos esto, también mostraremos la lista de los usuarios que vayamos agregando.
Si van a PHPMyAdmin observarán que un nuevo registro fue agregado a la tabla “users” de la base de datos pruebalaravel:

Espero que les haya gustado y hayan aprendido bastante con este tutorial. Dudas y preguntas en los comentarios.
Nos vemos la próxima semana.
¿Sabes SQL? ¿No-SQL? Aprende MySQL, PostgreSQL, MongoDB, Redis y más con el Curso Profesional de Bases de Datos que empieza el martes, en vivo.
Por rezme el 20 de Octubre de 2013
ErrorException
Undefined offset: 0
Al parecer esto se debe a la restriccion "unique" en el "email" probé quitandola y me funciono... sin embargo que puedo hacer para que me la acepte?
rezme :
ErrorException
Undefined offset: 0
Al parecer esto se debe a la restriccion "unique" en el "email" probé quitandola y me funciono... sin embargo que puedo hacer para que me la acepte?
Sí, a la regla "unique" le faltaba esto:
Código :
Tienes que especificar la tabla, en este caso "users"
Error mío, pediré que corrijan el tutorial
Por Mundoff el 27 de Octubre de 2013
Por Miguel Ángel Torres el 07 de Noviembre de 2013
Menos mal que hay profesores así en la red.
Gracias una vez más.
Por miguelangeltorres el 15 de Noviembre de 2013
Cuando hacemos el siguiente redirect :
return Redirect::route('admin.users.create')->withInput()->withErrors($user->errors);
Como es que luego dentro del método create no se borra el valor de $user al hacer $user = new User ???
Gracias y un saludo !!!
Como es que luego al re
Les comparto el link con las traducciones
Por tostado el 06 de Diciembre de 2013
Undefined index: full_name
No se que pueda ser
Por msanchezp el 11 de Diciembre de 2013
Por el 12 de Diciembre de 2013
https://github.com/caouecs/Laravel4-lang
Por jrperezz el 12 de Diciembre de 2013
Estoy usando un virtualhost es decir en ves de usar localhost/xxx/xxx modifique el hosts para solo usar http://x_lar/ entonces cuando uso este formulario el codigo:
return Redirect::route('admin.users.create')->withInput()->withErrors($user->errors);
redirige y coloca la direccíon -- http://x_lar/http://x_lar/admin/users/create
Alguien tiene una idea de como solucionar esto? por curiosidad porque cuando pongo la direccion completa http://localhost/laravel/public/ad... si funciona bien.
Gracias.
Por sergiowebdev el 30 de Diciembre de 2013
Symfony \ Component \ HttpKernel \ Exception \ NotFoundHttpException
No sé que puede estar fallando porque he revisado si me he dejado algo dos veces.
Por sergiowebdev el 30 de Diciembre de 2013
public function isValid($data)
{
$rules = array(
'email' => 'required|email|unique:users',
'full_name' => 'required|min:4|max:40',
'password' => 'required|min:8|confirmed'
);
$validator = Validator::make($data, $rules);
if ($validator->passes())
{
return true;
}
$this->errors = $validator->errors();
return false;
}
Por sergiowebdev el 30 de Diciembre de 2013
Mantener los valores especificados por el usuario
A mi me rebota al formulario con los datos introducidos presentes. No entiendo porque modificando lo otro me sale el error. Voy a seguir con el tutorial así a ver.
Por Edwin Moreno el 05 de Enero de 2014
Espero le sea útil, Salu2!! (a penas estoy haciendo el tuto)
¿Sabras alguna forma de solucionar esto y que muestre el mensaje de una forma amigable? Saludos.
Código :
Por Lasquetty el 27 de Enero de 2014
Otra duda que tengo es sobre mantener los valores especificados en el formulario. Sin usar Form::model dichos valores se me mantienen. ¿Puede ser esto cosa del navegador? Uso Firefox.
Por Lasquetty el 27 de Enero de 2014
1) Creo que por defecto los formularios de Laravel usan método POST, por lo que sería innecesario ponerlo explícitamente en el Form.
2) No tiene nada que ver con Laravel, pero ¿qué es eso de 'role' => 'form'?
Role => form es algo de Twitter Bootstrap. No tiene nada que ver con Laravel como tal.
Saludos!
Por Juan el 03 de Junio de 2014
public function create()
{
// Creamos un nuevo objeto User para ser usado por el helper Form::model
$user = new User;
return View::make('admin/users/form')->with('user', $user);
}
cuando coloco esto ->with('user', $user); que quiere decir gracias.
Por Gustavo Meza el 19 de Junio de 2014
estoy haciendo los tutoriales con slqServer 2005. Se crea la tabla con migraciones perfectamente, pero cuando llego a éste tutorial, hago todo exactamente igual (haciendo las modificaciones que mencionas).
Pero no me guarda en la tabla, al dar clic en "Crear usuario" valida correctamente los campos y
me arroja ésto: "Whoops, looks like something went wrong."
¿Hay que guardar de forma distinta si es SqlServer?
¿hay manera de ver cual es el error exacto?
Por juliobas el 12 de Julio de 2014
Por marcinf2 el 31 de Julio de 2014
{{-- Reemplazamos Form::open por Form::model: --}}
{{ Form::model($user, array('route' => 'admin.users.store', 'method' => 'POST'), array('role' => 'form')) }}
me tira
"Whoops, looks like something went wrong."
Por domenor el 23 de Agosto de 2014
Al igresar el email de esta forma [email protected] me lo acepta y lo graba, aunque tenga dos .com ¿Puede esto corregirse?
Gracias
Por Alejandro Mora el 20 de Octubre de 2014
array_forget($data, 'password_confirmation'); //Esto eliminar password_confirmation del arreglo data
Por Achipis el 23 de Diciembre de 2014
Tengo exactamente el mismo problema de Gustavo Meza...Todo marchaba bien hasta este capitulo. Trabajo con SQL Server
Agradezco tu ayuda!!