Validación de formularios
Elementos obligatorios
Marcamos los elementos obligatorios con el método setRequired()
, cuyo argumento es el texto del mensaje de error que se mostrará si el usuario no rellena el elemento. Si no se proporciona el
argumento, se utilizará el mensaje de error predeterminado.
$form->addText('name', 'Nombre:')
->setRequired('Por favor, introduzca su nombre');
Reglas
Añadimos reglas de validación a los elementos con el método addRule()
. El primer parámetro es la regla, el
segundo es el texto del 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 solo se verifican 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 usar estas reglas para todos los elementos:
constante | descripción | tipo de argumento |
---|---|---|
Required |
elemento obligatorio, alias para setRequired() |
– |
Filled |
elemento obligatorio, alias para setRequired() |
– |
Blank |
el elemento no debe ser rellenado | – |
Equal |
el valor es igual al parámetro | mixed |
NotEqual |
el valor no es igual al parámetro | mixed |
IsIn |
el valor es igual a uno de los elementos del array | array |
IsNotIn |
el valor no es igual a ninguno de los elementos del array | array |
Valid |
¿está el elemento rellenado correctamente? (para condiciones) | – |
Entradas de texto
Para los elementos addText()
, addPassword()
, addTextArea()
, addEmail()
,
addInteger()
, addFloat()
, también se pueden usar algunas de las siguientes reglas:
MinLength |
longitud mínima del texto | int |
MaxLength |
longitud máxima del texto | int |
Length |
longitud en un rango o longitud exacta | par [int, int] o int |
Email |
dirección de correo electrónico válida | – |
URL |
URL absoluta | – |
Pattern |
coincide con la expresión regular | string |
PatternInsensitive |
como Pattern , pero insensible a mayúsculas/minúsculas |
string |
Integer |
valor entero | – |
Numeric |
alias para Integer |
– |
Float |
número | – |
Min |
valor mínimo del elemento numérico | int|float |
Max |
valor máximo del elemento numérico | int|float |
Range |
valor en el rango | par [int|float, int|float] |
Las reglas de validación Integer
, Numeric
y Float
convierten directamente el valor a
entero o flotante, respectivamente. Además, la regla URL
también acepta una dirección sin esquema (p. ej.,
nette.org
) y añade el esquema (https://nette.org
). La expresión en Pattern
y
PatternIcase
debe aplicarse a todo el valor, es decir, como si estuviera envuelta en los caracteres ^
y
$
.
Número de elementos
Para los elementos addMultiUpload()
, addCheckboxList()
, addMultiSelect()
, también se
pueden usar 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 un rango o número exacto | par [int, int] o int |
Carga de archivos
Para los elementos addUpload()
, addMultiUpload()
, también se pueden usar las siguientes reglas:
MaxFileSize |
tamaño máximo del archivo en bytes | int |
MimeType |
Tipo MIME, se permiten comodines ('video/*' ) |
string|string[] |
Image |
imagen JPEG, PNG, GIF, WebP, AVIF | – |
Pattern |
el nombre del archivo coincide con la expresión regular | string |
PatternInsensitive |
como Pattern , pero insensible a mayúsculas/minúsculas |
string |
MimeType
e Image
requieren la extensión PHP fileinfo
. Detectan si el archivo o imagen
es del tipo requerido basándose en su firma y no verifican la integridad de todo el archivo. Si una imagen está dañada
se puede detectar, por ejemplo, intentando cargarla.
Mensajes de error
Todas las reglas predefinidas, excepto Pattern
y PatternInsensitive
, tienen un mensaje de error
predeterminado, por lo que se puede omitir. Sin embargo, al proporcionar y formular todos los mensajes a medida, hará que el
formulario sea más fácil de usar.
Puede cambiar los mensajes predeterminados en la configuración,
editando los textos en el array Nette\Forms\Validator::$messages
o usando un traductor.
En el texto de los mensajes de error se pueden usar estas cadenas de marcador de posición:
%d |
reemplaza secuencialmente con los argumentos de la regla |
%n$d |
reemplaza con el n-ésimo argumento de la regla |
%label |
reemplaza con la etiqueta del elemento (sin dos puntos) |
%name |
reemplaza con el nombre del elemento (p. ej. name ) |
%value |
reemplaza con 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 como máximo %d', [5, 10]);
$form->addInteger('id', 'ID:')
->addRule($form::Range, 'como máximo %2$d y al menos %1$d', [5, 10]);
Condiciones
Además de las reglas, también se pueden añadir condiciones. Se escriben de forma similar a las reglas, solo que en lugar de
addRule()
usamos el método addCondition()
y, por supuesto, no proporcionamos ningún mensaje de error
(la condición solo 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 dígito
->addRule($form::Pattern, 'Debe contener un dígito', '.*[0-9].*');
La condición también se puede vincular a un elemento diferente al actual usando addConditionOn()
. Como primer
parámetro, proporcionamos una referencia al elemento. En este ejemplo, el correo electrónico será obligatorio solo si se marca
la casilla de verificación (su valor será true
):
$form->addCheckbox('newsletters', 'Enviarme boletines');
$form->addEmail('email', 'E-mail:')
// si la casilla de verificación está marcada
->addConditionOn($form['newsletters'], $form::Equal, true)
// entonces requiere el correo electrónico
->setRequired('Introduzca una dirección de correo electrónico');
Se pueden crear estructuras complejas a partir de condiciones usando elseCondition()
y
endCondition()
:
$form->addText(/* ... */)
->addCondition(/* ... */) // si se cumple la primera condición
->addConditionOn(/* ... */) // y la segunda condición en otro elemento
->addRule(/* ... */) // requiere esta regla
->elseCondition() // si la segunda condición no se cumple
->addRule(/* ... */) // requiere estas reglas
->addRule(/* ... */)
->endCondition() // volvemos a la primera condición
->addRule(/* ... */);
En Nette, es muy fácil reaccionar al cumplimiento o incumplimiento de una condición también en el lado de JavaScript usando
el método toggle()
, consulte JavaScript dinámico.
Referencia a otro elemento
También se puede pasar otro elemento del formulario como argumento de una regla o condición. La regla entonces usará el
valor introducido posteriormente por el usuario en el navegador. De esta manera, se puede validar dinámicamente, por ejemplo, que
el elemento password
contenga la misma cadena que el elemento password_confirm
:
$form->addPassword('password', 'Contraseña');
$form->addPassword('password_confirm', 'Confirmar contraseña')
->addRule($form::Equal, 'Las contraseñas introducidas no coinciden', $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 simple!
Se puede pasar cualquier callback como primer parámetro a los métodos addRule()
o addCondition()
.
Este recibe el propio elemento como primer parámetro y devuelve un valor booleano que indica si la validación se realizó
correctamente. Al añadir una regla con addRule()
, también es posible especificar argumentos adicionales, que luego
se pasan como segundo parámetro.
Así podemos crear nuestro propio conjunto de validadores como una clase con métodos estáticos:
class MyValidators
{
// comprueba si el valor es divisible por el argumento
public static function validateDivisibility(Nette\Forms\Control $input, $arg): bool
{
return $input->getValue() % $arg === 0;
}
public static function validateEmailDomain(Nette\Forms\Control $input, $domain)
{
// otros validadores
}
}
El uso es entonces muy simple:
$form->addInteger('num')
->addRule(
[MyValidators::class, 'validateDivisibility'],
'El valor debe ser un múltiplo de %d',
8,
);
Las reglas de validación personalizadas también se pueden añadir a JavaScript. La condición 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 \
,
un guion bajo _
y el nombre del método. Por ejemplo, App\MyValidators::validateDivisibility
se escribe
como AppMyValidators_validateDivisibility
y se añade al objeto Nette.validators
:
Nette.validators['AppMyValidators_validateDivisibility'] = (elem, args, val) => {
return val % args === 0;
};
Evento onValidate
Después de enviar el formulario, se realiza la validación, donde se comprueban las reglas individuales añadidas mediante
addRule()
y luego se dispara el evento
onValidate
. Su handler se puede usar para validación adicional, típicamente para verificar la combinación correcta
de valores en múltiples elementos del formulario.
Si se detecta un error, lo pasamos al formulario con el método addError()
. Se puede llamar 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('Esta combinación no es posible.');
}
}
Errores durante el procesamiento
En muchos casos, nos enteramos del error solo cuando procesamos un formulario válido, por ejemplo, al escribir un nuevo
elemento en la base de datos y encontrar una duplicidad de claves. En tal caso, nuevamente pasamos el error al formulario con el
método addError()
. Se puede llamar 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('Contraseña inválida.');
}
}
Si es posible, recomendamos adjuntar el error directamente al elemento del formulario, ya que se mostrará junto a él al usar el renderizador predeterminado.
$form['date']->addError('Lo sentimos, pero esta fecha ya está ocupada.');
Puede llamar a addError()
repetidamente para pasar múltiples mensajes de error al formulario o elemento. Los
obtiene usando getErrors()
.
Tenga en cuenta que $form->getErrors()
devuelve un resumen de todos los mensajes de error, incluidos los que se
pasaron directamente a elementos individuales, no solo directamente al formulario. Los mensajes de error pasados solo al
formulario se obtienen a través de $form->getOwnErrors()
.
Modificación de la entrada
Usando el método addFilter()
, podemos modificar el valor introducido por el usuario. En este ejemplo, toleraremos
y eliminaremos los espacios en el código postal:
$form->addText('zip', 'Código Postal:')
->addFilter(function ($value) {
return str_replace(' ', '', $value); // eliminamos los espacios del código postal
})
->addRule($form::Pattern, 'El código postal no tiene el formato de cinco dígitos', '\d{5}');
El filtro se integra entre las reglas y condiciones de validación, por lo que el orden de los métodos importa, es decir, el
filtro y la regla se llaman en el mismo orden que los métodos addFilter()
y addRule()
.
Validación JavaScript
El lenguaje para formular condiciones y reglas es muy potente. Todas las construcciones funcionan tanto en el lado del servidor
como en el lado de JavaScript. Se transmiten en atributos HTML data-nette-rules
como JSON. La validación en sí la
realiza un script que captura el evento submit
del formulario, recorre los elementos individuales y realiza la
validación correspondiente.
Ese script es netteForms.js
y está disponible desde múltiples fuentes posibles:
Puede insertar el script directamente en la página HTML desde un CDN:
<script src="https://unpkg.com/nette-forms@3"></script>
O copiarlo localmente a la carpeta pública del proyecto (p. ej., desde
vendor/nette/forms/src/assets/netteForms.min.js
):
<script src="/path/to/netteForms.min.js"></script>
O instalarlo a través de npm:
npm install nette-forms
Y luego cargarlo y ejecutarlo:
import netteForms from 'nette-forms';
netteForms.initOnLoad();
Alternativamente, 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 para introducir la dirección solo si el usuario elige enviar la mercancía por correo? No hay
problema. La clave es el 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 este ID y al hacer clic en la casilla de verificación se ocultarán o mostrarán. Esto lo
asegura el script netteForms.js
.
Como argumento del método toggle()
, se puede pasar cualquier selector. Por razones históricas, una cadena
alfanumérica sin otros caracteres especiales se entiende como el ID del elemento, es decir, igual que si le precediera el
carácter #
. El segundo parámetro opcional permite invertir el comportamiento, es decir, si usáramos
toggle('#address-container', false)
, el elemento se mostraría solo si la casilla de verificación no estuviera
marcada.
La implementación predeterminada en JavaScript cambia la propiedad hidden
de los elementos. Sin embargo, podemos
cambiar fácilmente el comportamiento, por ejemplo, añadir una animación. Simplemente sobrescriba el método
Nette.toggle
en JavaScript con su propia solución:
Nette.toggle = (selector, visible, srcElement, event) => {
document.querySelectorAll(selector).forEach((el) => {
// ocultamos o mostramos 'el' según el valor de 'visible'
});
};
Desactivación de la validación
A veces puede ser útil desactivar la validación. Si al presionar un botón de envío no se debe realizar la validación
(adecuado para botones Cancelar o Vista previa), la desactivamos con el método
$submit->setValidationScope([])
. Si debe realizar solo una validación parcial, podemos especificar qué campos
o contenedores de formulario se deben 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['name']]); // Valida solo el elemento name
$form->addSubmit('send4')
->setValidationScope([$form['details']['age']]); // Valida solo el elemento age
$form->addSubmit('send5')
->setValidationScope([$form['details']]); // Valida el contenedor details
setValidationScope
no afecta al evento onValidate del formulario, que siempre
será llamado. El evento onValidate
de un contenedor solo se activará si ese contenedor está marcado para
validación parcial.