Validación de formularios
Controles obligatorios
Los controles se marcan como obligatorios con el método setRequired()
, cuyo argumento es el texto del mensaje de error que se mostrará si el usuario no lo rellena. Si no se da ningún argumento, se
utiliza el mensaje de error por defecto.
$form->addText('name', 'Nombre:')
->setRequired('Por favor, introduzca su nombre.');
Reglas
Añadimos reglas de validación a los controles con el método addRule()
. El primer parámetro es la regla, el
segundo es el mensaje de error y el tercero es el argumento de la regla de validación.
$form->addPassword('password', 'Contraseña:')
->addRule($form::MinLength, 'La contraseña debe tener al menos %d caracteres', 8);
**Las reglas de validación sólo se comprueban si el usuario ha rellenado el elemento.
Nette viene con una serie de reglas predefinidas cuyos nombres son constantes de la clase Nette\Forms\Form
.
Podemos aplicar estas reglas a todos los elementos:
constante | descripción | argumentos |
---|---|---|
Required |
alias de setRequired() |
– |
Filled |
Alias de setRequired() |
|
Blank |
no debe rellenarse | – |
Equal |
valor es igual al parámetro | mixed |
NotEqual |
valor no es ser igual a parámetro | mixed |
IsIn |
valor es igual a algún elemento del array | array |
IsNotIn |
El valor no es igual a ningún elemento de la matriz. array |
|
Valid |
la entrada pasa la validación (para condiciones) | – |
Entradas de texto
Para los elementos addText()
, addPassword()
, addTextArea()
, addEmail()
,
addInteger()
, addFloat()
también se pueden aplicar algunas de las siguientes reglas:
MinLength |
longitud mínima de cadena | int |
MaxLength |
longitud máxima de cadena | int |
Length |
longitud en el rango o longitud exacta | par [int, int] o int |
Email |
dirección de correo electrónico válida | – |
URL |
URL válida | – |
Pattern |
coincide con patrón regular | string |
PatternInsensitive |
como Pattern , pero sin distinguir mayúsculas de minúsculas |
string |
Integer |
número entero | – |
Numeric |
alias de Integer |
– |
Float |
número entero o de coma flotante | – |
MIN |
mínimo del valor entero | int|float |
MAX |
máximo del valor entero | int|float |
Range |
valor en el rango | par [int|float, int|float] |
Las reglas Integer
, Numeric
a Float
convierten automáticamente el valor a entero
(o flotante respectivamente). Además, la regla URL
también acepta una dirección sin esquema (por ejemplo,
nette.org
) y completa el esquema (https://nette.org
). Las expresiones de Pattern
y
PatternInsensitive
deben ser válidas para todo el valor, es decir, como si estuviera envuelto en los caracteres
^
and $
.
Número de artículos
Para los elementos addMultiUpload()
, addCheckboxList()
, addMultiSelect()
también puede
utilizar las siguientes reglas para limitar el número de elementos seleccionados o archivos cargados:
MinLength |
número mínimo | int |
MaxLength Número máximo int |
||
Length |
número en rango o número exacto | pares [int, int] o int |
Carga de archivos
Para los controles addUpload()
, addMultiUpload()
también se pueden utilizar las siguientes
reglas:
MaxFileSize |
tamaño máximo del archivo en bytes | int |
MimeType |
tipo de MIME, acepta comodines ('video/*' ) |
string|string[] |
Image |
el archivo cargado es JPEG, PNG, GIF, WebP | – |
Pattern |
nombre de archivo coincide con expresión regular | string |
PatternInsensitive |
como Pattern , pero sin distinguir entre mayúsculas y minúsculas |
string |
MimeType
y Image
requieren la extensión PHP fileinfo
. Si un archivo o imagen es del
tipo requerido se detecta por su firma. No se comprueba la integridad de todo el archivo. Puede averiguar si una imagen no está
dañada, por ejemplo, intentando cargarla.
Mensajes de error
Todas las reglas predefinidas excepto Pattern
y PatternInsensitive
tienen un mensaje de error por
defecto, por lo que pueden omitirse. Sin embargo, al pasar y formular todos los mensajes personalizados, hará que el formulario
sea más fácil de usar.
Puede cambiar los mensajes por defecto en configuration, modificando los
textos de la matriz Nette\Forms\Validator::$messages
o utilizando el
traductor.
Se pueden utilizar los siguientes comodines en el texto de los mensajes de error:
%d |
reemplaza gradualmente las reglas después de los argumentos |
%n$d |
sustituye por el enésimo argumento de regla |
%label |
sustituye por la etiqueta del campo (sin dos puntos) |
%name |
sustituye por el nombre del campo (por ejemplo, name ) |
%value |
sustituye por el valor introducido por el usuario. |
$form->addText('name', 'Nombre:')
->setRequired('Por favor, rellene %label');
$form->addInteger('id', 'ID:')
->addRule($form::Range, 'Al menos %d y no más de %d', [5, 10]);
$form->addInteger('id', 'ID:')
->addRule($form::Range, 'No más de %2$d y al menos %1$d', [5, 10]);
Condiciones
Además de las reglas de validación, se pueden establecer condiciones. Se establecen de forma muy parecida a las reglas,
aunque utilizamos addRule()
en lugar de addCondition()
y, por supuesto, lo dejamos sin mensaje de error
(la condición sólo pregunta):
$form->addPassword('password', 'Contraseña:')
// si la contraseña no tiene más de 8 caracteres ...
->addCondition($form::MaxLength, 8)
// ... entonces debe contener un número
->addRule($form::Pattern, 'Debe contener número', '.*[0-9].*');
La condición puede vincularse a un elemento distinto del actual utilizando addConditionOn()
. El primer parámetro
es una referencia al campo. En el siguiente caso, el correo electrónico sólo será necesario si la casilla de verificación
está marcada (es decir, su valor es true
):
$form->addCheckbox('boletines', 'enviarme boletines');
$form->addEmail('email', 'Email:')
// si la casilla está marcada ...
->addConditionOn($form['newsletters'], $form::Equal, true)
// ... requiere email
->setRequired('Introduzca su dirección de correo electrónico');
Las condiciones pueden agruparse en estructuras complejas con los métodos elseCondition()
y
endCondition()
.
$form->addText(/* ... */)
->addCondition(/* ... */) // si se cumple la primera condición
->addConditionOn(/* ... */) // y la segunda condición en otro elemento también
->addRule(/* ... */) // requiere esta regla
->elseCondition() // si no se cumple la segunda condición
->addRule(/* ... */) // requiere estas reglas
->addRule(/* ... */)
->endCondition() // volvemos a la primera condición
->addRule(/* ... */);
En Nette, es muy fácil reaccionar al cumplimiento o no de una condición en la parte JavaScript utilizando el método
toggle()
, véase JavaScript dinámico.
Referencia a otro elemento
Como argumento para una regla o condición, también puede pasar otro elemento del formulario. La regla utilizará entonces el
valor introducido posteriormente por el usuario en el navegador. Esto se puede utilizar, por ejemplo, para validar dinámicamente
que el elemento password
contiene la misma cadena que el elemento password_confirm
:
$form->addPassword('password', 'Password');
$form->addPassword('password_confirm', 'Confirm Password')
->addRule($form::Equal, 'The passwords do not match', $form['password']);
Reglas y condiciones personalizadas
A veces nos encontramos en una situación en la que las reglas de validación incorporadas en Nette no son suficientes y necesitamos validar los datos del usuario a nuestra manera. En Nette esto es muy fácil.
Puede pasar cualquier callback como primer parámetro a los métodos addRule()
o addCondition()
. El
callback acepta el propio elemento como primer parámetro y devuelve un valor booleano que indica si la validación se ha
realizado correctamente. Cuando se añade una regla utilizando addRule()
, se pueden pasar argumentos adicionales, que
se pasan como segundo parámetro.
De este modo, el conjunto personalizado de validadores puede crearse como una clase con métodos estáticos:
class MyValidators
{
// comprueba si el valor es divisible por el argumento
public static function validateDivisibility(BaseControl $input, $arg): bool
{
return $input->getValue() % $arg === 0;
}
public static function validateEmailDomain(BaseControl $input, $domain)
{
// validadores adicionales
}
}
El uso es muy sencillo:
$form->addInteger('num')
->addRule(
[MyValidators::class, 'validateDivisibility'],
'The value must be a multiple of %d',
8,
);
Las reglas de validación personalizadas también se pueden añadir a JavaScript. El único requisito es que la regla sea un
método estático. Su nombre para el validador JavaScript se crea concatenando el nombre de la clase sin barras invertidas
\
, the underscore _
, y el nombre del método. Por ejemplo, escriba
App\MyValidators::validateDivisibility
como AppMyValidators_validateDivisibility
y añádalo al objeto
Nette.validators
:
Nette.validators['AppMyValidators_validateDivisibility'] = (elem, args, val) => {
return val % args === 0;
};
Evento onValidate
Una vez enviado el formulario, la validación se realiza comprobando las reglas individuales añadidas por
addRule()
y llamando al evento onValidate
.
Su manejador puede ser usado para validación adicional, típicamente para verificar la correcta combinación de valores en
múltiples elementos del formulario.
Si se detecta un error, se pasa al formulario utilizando el método addError()
. Este puede ser llamado en un
elemento específico o directamente en el formulario.
protected function createComponentSignInForm(): Form
{
$form = new Form;
// ...
$form->onValidate[] = [$this, 'validateSignInForm'];
return $form;
}
public function validateSignInForm(Form $form, \stdClass $data): void
{
if ($data->foo > 1 && $data->bar > 5) {
$form->addError('This combination is not possible.');
}
}
Tratamiento de errores
En muchos casos, descubrimos un error cuando estamos procesando un formulario válido, por ejemplo, cuando escribimos una nueva
entrada en la base de datos y nos encontramos con una clave duplicada. En este caso, devolvemos el error al formulario utilizando
el método addError()
. Este método puede ejecutarse en un elemento específico o directamente en el formulario:
try {
$data = $form->getValues();
$this->user->login($data->username, $data->password);
$this->redirect('Home:');
} catch (Nette\Security\AuthenticationException $e) {
if ($e->getCode() === Nette\Security\Authenticator::InvalidCredential) {
$form->addError('Invalid password.');
}
}
Si es posible, recomendamos añadir el error directamente al elemento del formulario, ya que aparecerá junto a él cuando se utilice el renderizador por defecto.
$form['date']->addError('Sorry, this date is already taken.');
Puedes llamar repetidamente a addError()
para pasar múltiples mensajes de error a un formulario o elemento. Se
obtienen con getErrors()
.
Tenga en cuenta que $form->getErrors()
devuelve un resumen de todos los mensajes de error, incluso los pasados
directamente a elementos individuales, no sólo directamente al formulario. Los mensajes de error pasados sólo al formulario se
recuperan mediante $form->getOwnErrors()
.
Modificación de los valores de entrada
Utilizando el método addFilter()
, podemos modificar el valor introducido por el usuario. En este ejemplo,
toleraremos y eliminaremos espacios en el código postal:
$form->addText('zip', 'Postcode:')
->addFilter(function ($value) {
return str_replace(' ', '', $value); // remove spaces from the postcode
})
->addRule($form::Pattern, 'The postal code is not five digits', '\d{5}');
El filtro se incluye entre las reglas de validación y las condiciones y por lo tanto depende del orden de los métodos, es
decir, el filtro y la regla se llaman en el mismo orden que el de los métodos addFilter()
y
addRule()
.
Validación JavaScript
El lenguaje de reglas y condiciones de validación es poderoso. Aunque todas las construcciones funcionan tanto del lado del
servidor como del lado del cliente, en JavaScript. Las reglas se transfieren en atributos HTML data-nette-rules
como
JSON. La validación en sí es manejada por otro script, que engancha todos los eventos del formulario submit
, itera
sobre todas las entradas y ejecuta las validaciones respectivas.
Este script es netteForms.js
, que está disponible en varias fuentes posibles:
Puedes incrustar el script directamente en la página HTML desde el CDN:
<script src="https://unpkg.com/nette-forms@3"></script>
O copiarlo localmente en la carpeta pública del proyecto (por ejemplo, desde
vendor/nette/forms/src/assets/netteForms.min.js
):
<script src="/path/to/netteForms.min.js"></script>
O instalar a través de npm:
npm install nette-forms
Y luego cargar y ejecutar:
import netteForms from 'nette-forms';
netteForms.initOnLoad();
También puede cargarlo directamente desde la carpeta vendor
:
import netteForms from '../path/to/vendor/nette/forms/src/assets/netteForms.js';
netteForms.initOnLoad();
JavaScript dinámico
¿Quiere mostrar los campos de dirección sólo si el usuario elige enviar la mercancía por correo? No hay problema. La clave
es un par de métodos addCondition()
& toggle()
:
$form->addCheckbox('send_it')
->addCondition($form::Equal, true)
->toggle('#address-container');
Este código dice que cuando se cumple la condición, es decir, cuando la casilla de verificación está marcada, el elemento
HTML #address-container
será visible. Y viceversa. Así, colocamos los elementos del formulario con la dirección
del destinatario en un contenedor con ese ID, y cuando se pulsa la casilla de verificación, se ocultan o se muestran. De esto se
encarga el script netteForms.js
.
Se puede pasar cualquier selector como argumento al método toggle()
. Por razones históricas, una cadena
alfanumérica sin otros caracteres especiales se trata como un ID de elemento, igual que si fuera precedida por el #
character. The second optional parameter allows us to reverse the behavior, i.e. if we used
toggle('#address-container', false)
, el elemento se mostraría sólo si la casilla de verificación estuviera
desmarcada.
La implementación por defecto de JavaScript cambia la propiedad hidden
para los elementos. Sin embargo, podemos
cambiar fácilmente el comportamiento, por ejemplo añadiendo una animación. Basta con anular el método
Nette.toggle
en JavaScript con una solución personalizada:
Nette.toggle = (selector, visible, srcElement, event) => {
document.querySelectorAll(selector).forEach((el) => {
// ocultar o mostrar 'el' según el valor de 'visible
});
};
Desactivación de la validación
En algunos casos, es necesario desactivar la validación. Si un botón de envío no se supone que ejecute la validación
después del envío (por ejemplo Cancelar o Preview), puedes desactivar la validación llamando a
$submit->setValidationScope([])
. También puede validar el formulario parcialmente especificando los elementos a
validar.
$form->addText('name')
->setRequired();
$details = $form->addContainer('details');
$details->addInteger('age')
->setRequired('age');
$details->addInteger('age2')
->setRequired('age2');
$form->addSubmit('send1'); // Valida todo el formulario
$form->addSubmit('send2')
->setValidationScope([]); // No valida nada
$form->addSubmit('send3')
->setValidationScope([$form['nombre']]); // Valida sólo el campo 'nombre
$form->addSubmit('send4')
->setValidationScope([$form['details']['age']]); // Valida sólo el campo 'edad
$form->addSubmit('send5')
->setValidationScope([$form['detalles']]); // Valida el contenedor 'detalles
El evento onValidate en el formulario se invoca siempre y no se ve afectado por
setValidationScope
. El evento onValidate
en el contenedor se invoca sólo cuando este contenedor está
especificado para validación parcial.