La validación de los datos es una parte importante de cualquier aplicación, ya que asegura que los datos en un modelo están conformes a las reglas de negocio de la aplicación. Por ejemplo, tu podrias querer que los passwords tengan a lo menos un largo de ocho caracteres, o asegurar que los username sean únicos. Definir reglas de validación hace que el manejo de los formularios sea muchísimo más fácil.
Hay muchos diferentes aspectos del proceso de validación. En esta sección cubriremos el lado del modelo, es decir, lo que ocurre cuando tu llamas al método save() de tu modelo. Para más información acerca de cómo manejar el despliegue de errores de validación, revisa la sección que cubre el FormHelper.
El primer paso en la validación de datos es la creación de las reglas de validación en el Modelo. Para hacer eso, usa el arreglo Model::validate en la definición del Modelo, por ejemplo:
<?php
class User extends AppModel {
var $name = 'User';
var $validate = array();
}
?>
En el ejemplo de arriba, el arreglo $validate se agrega al modelo User, pero el arreglo no contiene reglas de validación. Asumiendo que la tabla users tiene los campos login, password, email y born, el ejemplo de abajo muestra algunas simples reglas de validación que se aplican a esos campos:
<?php
class User extends AppModel {
var $name = 'User';
var $validate = array(
'login' => 'alphaNumeric',
'email' => 'email',
'born' => 'date'
);
}
?>
El ejemplo muestra cómo se pueden agregar reglas de validación a los campos de un modelo. Para el campo login serán aceptadas sólo letras y números, el email debe ser válido, y born debe ser una fecha válida. La definición de reglas de validación habilitan en CakePHP el despliegue automático de mensajes de error en formularos si los datos enviados no cumplen las reglas de validación.
CakePHP incluye muchas reglas de validación y usarlas puede ser bastante simple. Algunas de las reglas incluidas permiten verificar el formato de los emails, URLs, y números de tarjeta de crédito - las cubriremos en detalle más adelante.
Acá tenemos un ejemplo de validación más complejo que aprovecha algunas de las reglas incluidas:
<?php
class User extends AppModel {
var $name = 'User';
var $validate = array(
'login' => array(
'alphaNumeric' => array(
'rule' => 'alphaNumeric',
'required' => true,
'message' => 'Sólo letras y números'
),
'between' => array(
'rule' => array('between', 5, 15),
'message' => 'Entre 5 y 15 caracteres'
)
),
'password' => array(
'rule' => array('minLength', '8'),
'message' => 'Largo mínimo de 8 caracteres'
),
'email' => 'email',
'born' => array(
'rule' => 'date',
'message' => 'Ingrese una fecha válida',
'allowEmpty' => true
)
);
}
?>
Dos reglas de validación son definidas para login: debería contener sólo letras y números, y su largo debe ser de 5 a 15. El campo password debe tener un largo mínimo de 8 caracteres. El email debe contener una dirección de correo válida, y born debe ser una fecha válida. Además, notar que puedes agregar mensajes de error propios que CakePHP mostrará cuando estas reglas de validación no se cumplan.
Como lo muestra el ejemplo de arriba, un único campo puede tener múltiples reglas de validación. Y si las reglas incluidas no coinciden con lo que necesitas, puedes agregar tus propias reglas de validación según tus requerimientos.
Ahora que viste a grandes rasgos cómo funciona la validación, veamos cómo estas reglas son definidas en el modelo. Hay tres diferentes maneras para definir reglas de validación: arreglos simples, una única regla por campo, y múltiples reglas por campo.
Tal como el nombre lo sugiere, esta es la manera más simple de definir una regla de validación. La sintaxis para la definición de reglas usando esta manera es:
var $validate = array('fieldName' => 'ruleName');
Donde, “fieldName” es el nombre del campo para el cual se está definiendo una regla, y “ruleName” es el nombre de una regla pre-definida (cubriremos esto en la siguiente sección).
Ésta técnica de definición permite un mejor control del funcionamiento de las reglas de validación. Pero antes de su discusión, veamos el patrón de uso general para agregar una regla a un solo campo:
var $validate = array(
'fieldName1' => array(
'rule' => 'ruleName', // ó: array('ruleName', 'param1', 'param2' ...)
'required' => true,
'allowEmpty' => false,
'on' => 'create', // ó: 'update'
'message' => 'Su mensaje de error'
)
);
El índice “rule” es requerido. Si sólo se setea “required” => true la validación del formulario no funcionará correctamente. Esto debido a que “required” no es en realidad una regla.
Como puedes ver, cada campo (arriba se está mostrando sólo un campo) es asociado con un arreglo que contiene cinco índice: ‘rule’, ‘required’, ‘allowEmpty’, ‘on’ y ‘message’. Veamos con más detalle cada uno de estos índices.
El índice ‘rule’ define el método de validación y acepta un sólo valor o un arreglo. El valor para ‘rule’ especificado puede ser el nombre de un método en tu modelo, un método de la clase core Validation, o una expresión regular. Para un completo listado de todas las reglas incorporadas ver la sección llamada «Reglas de Validación Incorporadas».
Si la regla no requiere parámetros, ‘rule’ puede ser un sólo valor, por ejemplo:
var $validate = array(
'login' => array(
'rule' => 'alphaNumeric'
)
);
Si la regla requiere algunos parámetros (como max, min o range), entonces ‘rule’ debería ser un arreglo:
var $validate = array(
'password' => array(
'rule' => array('minLength', 8)
)
);
Recuerda, el índice ‘rule’ es requerido para la definición de reglas basadas en arreglos.
Este índice debería tener asignado un valor booleano. Si ‘required’ es true, el campo debe estar presente en el arreglo de datos. Por ejemplo, si la regla de validación ha sido definida de la siguiente manera:
var $validate = array(
'login' => array(
'rule' => 'alphaNumeric',
'required' => true
)
);
Los datos enviados al método save() del modelo deben contener un valor para el campo login. Si no es así, la validación falla (la regla no se cumple). El valor por defecto para este índice es un false booleano.
Si el índice login están presente pero no tiene un valor asignado, la validación será exitosa. Setear ‘required’ a true sólo verifica que el índice del campo este presente.
Al índice allowEmpty
se le debería asignar un valor booleano. Si
allowEmpty
es false, los datos pasados al método save()
del
modelo deben incluir el campo a un valor no-vacío para ese campo. Cuando
se deja en true, un campo vacío pasará exitosamente la validación de ese
campo.
El valor por defecto de allowEmpty
es null. Esto significa que el
campo siempre procesará las reglas de validación incluyendo la ejecución
de funciones de validación propias.
El índice ‘on’ puede ser seteado con uno de los siguientes valores: ‘update’ o ‘create’. Esto provee un mecanismo que permite que cierta regla sea aplicada ya sea durante la creación de un nuevo registro, o durante la actualización de un registro.
Si la regla fue definida como ‘on’ => ‘create’, entonces la regla sólo será verificada durante la creación de un nuevo registro. De igual manera, si es definida como ‘on’ => ‘update’, la regla sólo será verificada durante la actualización de un registro.
El valor por defecto de ‘on’ es null. Cuando ‘on’ es null, la regla será verificada durante la creación y actualización de un registro.
El índice ‘message’ permite definir un mensaje de error de validación para la regla:
var $validate = array(
'password' => array(
'rule' => array('minLength', 8),
'message' => 'Password debe tener a lo menos 8 caracteres'
)
);
La técnica descrita anteriormente nos entrega mayor flexibilidad que la asignación de reglas simples, pero hay un paso adicional que podemos tomar para lograr un control más fino de la validación de datos. La siguiente técnica que revisaremos nos permite asignar múltiples reglas de validación por cada campo de un modelo.
Si quieres asignar múltiples reglas de validación a un sólo campo, básicamente así es cómo se verá:
var $validate = array(
'nombreCampo' => array(
'nombreRegla' => array(
'rule' => 'nombreRegla',
// acá van índices extra como on, required, etc.
),
'nombreRegla2' => array(
'rule' => 'nombreRegla2',
// acá van índices extra como on, required, etc.
)
)
);
Como puedes ver, esto es bastante similar a lo que hicimos en la sección previa. Anteriormente, por cada campo teníamos un sólo arreglo con parámetros de validación. En este caso, cada ‘nombreCampo’ consiste en un arreglo de índices de reglas. Cada ‘nombreRegla’ contiene un arreglo distinto con parámetros de validación.
Esto se entiende mejor con un ejemplo práctico:
var $validate = array(
'login' => array(
'alphanumeric' => array(
'rule' => 'alphaNumeric',
'message' => 'Se permiten sólo letras y números',
'last' => true
),
'minlength' => array(
'rule' => array('minLength', '8'),
'message' => 'Largo mínimo de 8 caracteres'
),
)
);
El ejemplo de arriba define dos reglas para el campo login: alphanumeric y minLength. Como puedes ver, cada regla se identifica con un nombre de índice. En este caso particular, los nombres de índice son similares a las reglas que usan, pero el nombre de índice puede ser cualquier nombre.
Por defecto CakePHP trata de validar un campo usando todas las reglas de
validación declaradas para él y retorna un mensaje de error para la
última regla no satisfecha. Pero si el índice last
es dejado como
true
y la regla no es satisfecha, entonces se mostrará el mensaje de
error para esa regla y no se validará ninguna regla adicional. Asi que
si prefieres mostrar un mensaje de error para la primera regla no
satisfecha entonces debes dejar 'last' => true
por cada regla.
Si vas a usar mensajes de error internacionalizados podrias quierer especificar los mensajes de error en las vistas:
echo $form->input('login', array(
'label' => __('Login', true),
'error' => array(
'alphanumeric' => __('Se permiten sólo letras y números', true),
'minlength' => __('Largo mínimo de 8 caracteres', true)
)
)
);
El campo ahora está totalmente internacionalizado, y puedes eliminar los mensajes del modelo. Para más información acerca de la función __() ver «Localization & Internationalization»
La clase Validation de CakePHP contiene muchas reglas de validación incorporadas que pueden hacer mucho más fácil la validación de datos. Esta clase contiene muchas técnicas de validación frecuentemente usadas que no necesitarás escribir por tu cuenta. Abajo encontrarás una lista completa de todas las reglas, junto ejemplos de uso.
Los datos para el campo deben contener sólo letras y números.
var $validate = array(
'login' => array(
'rule' => 'alphaNumeric',
'message' => 'Los nombres de usuario deben contener sólo letras y números.'
)
);
El largo de los datos para el campo debe estar dentro de un rango numérico específico. Se debe indicar un valor mínimo y máximo.
var $validate = array(
'password' => array(
'rule' => array('between', 5, 15),
'message' => 'Las contraseñas deben tener un largo entre 5 y 15 caracteres.'
)
);
Esta regla es usada para asegurar que el campo es dejado en blanco o con sólo espacios en blanco como su valor. Los espacios en blanco incluyen los caracteres de la barra espaciadora, tabulador, retorno de carro y nueva línea.
var $validate = array(
'id' => array(
'rule' => 'blank',
'on' => 'create'
)
);
El campo debe contener un valor booleano. Los valores aceptados son “true” o “false”, los enteros 0 o 1 y las cadenas «0» o «1».
var $validate = array(
'myCheckbox' => array(
'rule' => array('boolean'),
'message' => 'Valor incorrecto en myCheckbox'
)
);
Esta regla es usada para verificar si los datos corresponden a un número de tarjeta de credito válido. Acepta tres parámetros: ‘type’, ‘deep’ y ‘regex’.
El ‘type’ puede ser ‘fast’, ‘all’ o cualquiera de los siguientes:
bankcard
diners
disc
electron
enroute
jcb
maestro
mc
solo
switch
visa
voyager
Si ‘type’ es dejado en ‘fast’, se validan los datos contra el formato numérico de las principales tarjetas de crédito. También se puede dejar ‘type’ como un arreglo con todos los tipos de validaciones que se quiere satisfacer.
El índice ‘deep’ debería dejarse con un valor booleano. Si es verdadero, la validación usará el algoritmo de Luhn para tarjetas de crédito (https://en.wikipedia.org/wiki/Luhn_algorithm). Por defecto el valor se asume como falso.
El índice ‘regex’ permite indicar una expersión regular propia que será usada para validar el número de tarjeta de credito.
var $validate = array(
'ccnumber' => array(
'rule' => array('cc', array('visa', 'maestro'), false, null),
'message' => 'El número de tarjeta de crédito que ha suministrado no es válido.'
)
);
Esta regla es usada para comparar valores numéricos. Soporta “is greater”, “is less”, “greater or equal”, “less or equal”, “is less”, “equal to”, y “not equal”. A continuación algunos ejemplos:
var $validate = array(
'age' => array(
'rule' => array('comparison', '>=', 18),
'message' => 'Debe tener al menos 18 años para calificar.'
)
);
var $validate = array(
'age' => array(
'rule' => array('comparison', 'greater or equal', 18),
'message' => 'Debe tener al menos 18 años para calificar.'
)
);
Esta regla asegura que los datos enviados esten en un formato de fecha válido. Un único parámetro (que puede ser un arreglo) puede ser pasado y que será usado para verificar el formato de la fecha indicada. El valor del parámetro puede ser uno de los siguientes formatos:
‘dmy’ por ejemplo 27-12-2006 o 27-12-06 (los separadores pueden ser espacio, punto, guion, slash)
‘mdy’ por ejemplo 12-27-2006 or 12-27-06 (los separadores pueden ser espacio, punto, guion, slash)
‘ymd’ por ejemplo 2006-12-27 or 06-12-27 (los separadores pueden ser espacio, punto, guion, slash)
‘dMy’ por ejemplo 27 December 2006 o 27 Dec 2006
‘Mdy’ por ejemplo December 27, 2006 o Dec 27, 2006 (la coma es opcional)
‘My’ por ejemplo (December 2006 o Dec 2006)
‘my’ por ejemplo 12/2006 o 12/06 (los separadores pueden ser espacio, punto, guion, slash)
Si no especifica ningún índice, se usará el índice por defecto ‘ymd’.
var $validate = array(
'born' => array(
'rule' => 'date',
'message' => 'Ingrese una fecha válida usando el formato AA-MM-DD.',
'allowEmpty' => true
)
);
Mientras que muchos almacenes de datos (motores de bases de datos) requieren cierto formato de datos, podrias considerar aceptar una amplia variedad de formatos de fechas y luego convertirlos, en vez de forzar a los usuarios a ingresar cierto formato. Entre más trabajo puedas hacer por tus usuarios, mejor.
Esta regla asegura que el dato es un número decimal válido. Se puede pasar un parámetro para especificar la cantidad de dígitos requeridos después del punto decimal. Si no se pasa ningún parámetro, el dato será validado como un número de punto flotante científico, que causará que la validación no sea satisfecha si es que no se encuentra ningún dígito después del punto decimal.
var $validate = array(
'price' => array(
'rule' => array('decimal', 2)
)
);
Esta regla verifica que el dato sea una dirección de correo electrónico válida. Al pasar un valor booleano verdadero como segundo parámetro se tratará también de verificar que el host de la dirección sea válido.
var $validate = array('email' => array('rule' => 'email'));
var $validate = array(
'email' => array(
'rule' => array('email', true),
'message' => 'Por favor indique una dirección de correo electrónico válida.'
)
);
Esta regla asegura que el valor sea equivalente a, y del mismo tipo que el valor indicado.
var $validate = array(
'food' => array(
'rule' => array('equalTo', 'cake'),
'message' => 'El valor debe ser el string cake'
)
);
Esta regla verifica que la extensión de archivo sea como .jpg o .png. Para permitir múltiples extensiones estas se deben pasar dentro de un arreglo.
var $validate = array(
'image' => array(
'rule' => array('extension', array('gif', 'jpeg', 'png', 'jpg'),
'message' => 'Por favor indique una imágen válida.'
)
);
Esta sección aún tiene que ser escrita, si tienes una idea de qué poner aqui, por favor usa los links y déjanos saber tu sugerencia!
Esta regla asegura que haya sido ingresada una dirección IPv4 válida.
var $validate = array(
'clientip' => array(
'rule' => 'ip',
'message' => 'Por favor ingrese una dirección IP válida.'
)
);
El dato para este campo debe ser único, no puede ser usado por ningún otro registro.
var $validate = array(
'login' => array(
'rule' => 'isUnique',
'message' => 'Este nombre de usuario ya ha sido asignado.'
)
);
Esta regla asegura que el dato cumple con un requisito de largo mínimo.
var $validate = array(
'login' => array(
'rule' => array('minLength', '8'),
'message' => 'Los nombres de usuario deben tener un largo de al menos 8 caracteres.'
)
);
Esta regla asegura que el dato siempre esté dentro del requisito de largo máximo.
var $validate = array(
'login' => array(
'rule' => array('maxLength', '15'),
'message' => 'Los nombres de usuario no pueden tener un largo mayor a 15 caracteres.'
)
);
Esta regla asegura que el valor sea una cantidad en formato monetario válido.
El segundo parámetro define dónde se ubica el símbolo: left/right (izquierda/derecha).
var $validate = array(
'salary' => array(
'rule' => array('money', 'left'),
'message' => 'Por favor ingrere una cantidad monetaria válida.'
)
);
Empleado para validar campos input select multiple. Soporta los paramentros «in», «max» y «min».
var $validate = array(
'multiple' => array(
'rule' => array('multiple', array('in' => array('foo', 'bar'), 'min' => 1, 'max' => 3)),
'message' => 'Por favor seleccione una, dos o tres opciones'
)
);
Esta regla asegura que el valor está dentro de un conjunto dado. Necesita de un arreglo de valores. El valor es válido si coincide con uno de los valores del arreglo indicado.
Example:
var $validate = array(
'function' => array(
'allowedChoice' => array(
'rule' => array('inList', array('Foo', 'Bar')),
'message' => 'Ingreso Foo o ingrese Bar.'
)
)
);
Verifica si el dato ingresado es un número válido.
var $validate = array(
'cars' => array(
'rule' => 'numeric',
'message' => 'Por favor indique la cantidad de vehículos.'
)
);
Regla básica para asegurar que un campo no este vacío.
var $validate = array(
'title' => array(
'rule' => 'notEmpty',
'message' => 'Este campo no puede quedar vacío.'
)
);
Phone valida números telefónicos de EE.UU. Si quieres validar números telefónicos que no sean de EE.UU. puedes proveer una expresión regular como segundo parámetro para cubrir formatos adicionales.
var $validate = array(
'phone' => array(
'rule' => array('phone', null, 'us')
)
);
Postal es usado para validar códigos ZIP de EE.UU. (us), Canada (ca), Reino Unido (uk), Italia (it), Alemania (de) y Bélgica (be). Para otros formatos ZIP puedes proveer una expersión regular como segundo parámetro.
var $validate = array(
'zipcode' => array(
'rule' => array('postal', null, 'us')
)
);
Esta regla asegura que el valor esté dentro de un rango dado. Si no se indica un rango, la regla va a verificar si el valor es un número finito válido en la actual plataforma.
var $validate = array(
'number' => array(
'rule' => array('range', 0, 10),
'message' => 'Por favor ingrese un número entre 0 y 10'
)
);
El ejemplo de arriba aceptará cualquier valor mayor a 0 (por ejemplo 0.01) y menor a 10 (por ejemplo 9.99).
Ssn valida los números de seguridad social de EE.UU. (us), Dinamarca (dk), y los Paises Bajos (nl). Para otros formatos de números de seguridad social puedes proveer una expersión regular.
var $validate = array(
'ssn' => array(
'rule' => array('ssn', null, 'us')
)
);
Esta regla verifica formatos de URL válidos. Soporta los protocolos http(s), ftp(s), file, news, y gopher.
var $validate = array(
'website' => array(
'rule' => 'url'
)
);
Si hasta el momento no has encontrado lo que buscabas, siempre podrás crear tus propias reglas de validación personalizadas. Hay dos maneras de hacer esto: definiendo expresiones regulares personalizadas, o creando métodos de validación personalizados.
Si la técnica de validación que necesitas usar puede ser completada usando expresiones regulares, puedes definir una expresión personalizada como una regla de validación de un campo.
var $validate = array(
'login' => array(
'rule' => array('custom', '/^[a-z0-9]{3,}$/i'),
'message' => 'Sólo letras y enteros, mínimo 3 caracteres'
)
);
El ejemplo de arriba verifica si login contiene sólo letras y enteros, con un largo mínimo de tres caracteres.
Algunas veces revisar los datos usando expresiones regulares no es suficiente. Por ejemplo, si quieres asegurar que un código promocional sólo pueda ser usado 25 veces, necesitas agregar una función de validación personalizada, como se muestra más abajo:
<?php
class User extends AppModel {
var $name = 'User';
var $validate = array(
'promotion_code' => array(
'rule' => array('limitDuplicates', 25),
'message' => 'Este código ha sido usado demasiadas veces.'
)
);
function limitDuplicates($data, $limit){
$existing_promo_count = $this->find( 'count', array('conditions' => $data, 'recursive' => -1) );
return $existing_promo_count < $limit;
}
}
?>
Si quieres pasar parámetros a tu función de validación personalizada,
agrega elementos extra al arreglo ‘rule’ y trátalos como parámetros
extra (despúes del parámetro principal $data
) en tu función
personalizada.
Tu función de validación personalizada puede estar en el modelo (como en el ejemplo de arriba), o en un behavior implementado por el modelo. Esto incluye los modelos mapeados.
Notar que los métodos del model/behavior son verificados primero, antes
de buscar un método en la clase Validation
. Esto significa que
puedes sobreescribir métodos de validación existentes (como por ejemplo
alphaNumeric()
) en el nivel de aplicación (agregando el método a
AppModel
), o en el nivel de modelo.
Mientras que normalmente sólo usarás el método save del modelo, habrán veces que te gustaría validar los datos sin guardarlos. Por ejemplo, podrías querer mostrar algo de información adicional al usuario antes de guardar los datos a la base de datos. Validar datos requiere de un proceso ligeramente distinto al de sólo guardar los datos.
Primero, debes setear los datos al modelo:
$this->ModelName->set( $this->data );
Luego, para verificar si los datos se validan correctamente, usa el método validates del modelo, que retornará true si es que se valida y false si es que no:
if ($this->ModelName->validates()) {
// paso la lógica de validación
} else {
// no paso la lógica de validadición
}
El método validates invoca el método invalidFields que le asignará un valor a la propiedad validationErrors del modelo. El método invalidFields también retorna los datos como su resultado.
$errors = $this->ModelName->invalidFields(); // contiene el arrego validationErrors
Es importante notar que los datos se deben primero setear al modelo antes de poder validarlos. Esto es diferente al método save que permite pasar los datos como un parámetro. También, ten en cuenta que no es necesario llamar a validates antes de llamar a save ya que save validará automáticamente los datos antes realmente de guardarlos.