Validation des formulaires
Éléments obligatoires
Nous marquons les éléments obligatoires avec la méthode setRequired()
, dont l'argument est le texte du message d'erreur qui s'affiche si l'utilisateur ne remplit pas l'élément. Si nous n'indiquons
pas d'argument, le message d'erreur par défaut est utilisé.
$form->addText('name', 'Nom:')
->setRequired('Veuillez saisir un nom');
Règles
Nous ajoutons des règles de validation aux éléments avec la méthode addRule()
. Le premier paramètre est la
règle, le deuxième est le texte du message d'erreur et le troisième est l'argument de la
règle de validation.
$form->addPassword('password', 'Mot de passe:')
->addRule($form::MinLength, 'Le mot de passe doit comporter au moins %d caractères', 8);
Les règles de validation ne sont vérifiées que si l'utilisateur a rempli l'élément.
Nette est livré avec un certain nombre de règles prédéfinies, dont les noms sont des constantes de la classe
Nette\Forms\Form
. Nous pouvons utiliser ces règles pour tous les éléments :
constante | description | type d'argument |
---|---|---|
Required |
élément obligatoire, alias pour setRequired() |
– |
Filled |
élément obligatoire, alias pour setRequired() |
– |
Blank |
l'élément ne doit pas être rempli | – |
Equal |
la valeur est égale au paramètre | mixed |
NotEqual |
la valeur n'est pas égale au paramètre | mixed |
IsIn |
la valeur est égale à l'un des éléments du tableau | array |
IsNotIn |
la valeur n'est égale à aucun des éléments du tableau | array |
Valid |
l'élément est-il rempli correctement ? (pour conditions) | – |
Entrées textuelles
Pour les éléments addText()
, addPassword()
, addTextArea()
, addEmail()
,
addInteger()
, addFloat()
, certaines des règles suivantes peuvent également être utilisées :
MinLength |
longueur minimale du texte | int |
MaxLength |
longueur maximale du texte | int |
Length |
longueur dans une plage ou longueur exacte | paire [int, int] ou int |
Email |
adresse e-mail valide | – |
URL |
URL absolue | – |
Pattern |
correspond à l'expression régulière | string |
PatternInsensitive |
comme Pattern , mais insensible à la casse |
string |
Integer |
valeur entière | – |
Numeric |
alias pour Integer |
– |
Float |
nombre | – |
Min |
valeur minimale de l'élément numérique | int|float |
Max |
valeur maximale de l'élément numérique | int|float |
Range |
valeur dans une plage | paire [int|float, int|float] |
Les règles de validation Integer
, Numeric
et Float
convertissent directement la valeur
en entier resp. flottant. De plus, la règle URL
accepte également une adresse sans schéma (par ex.
nette.org
) et complète le schéma (https://nette.org
). L'expression dans Pattern
et
PatternIcase
doit s'appliquer à toute la valeur, c'est-à-dire comme si elle était entourée des caractères
^
et $
.
Nombre d'éléments
Pour les éléments addMultiUpload()
, addCheckboxList()
, addMultiSelect()
, les règles
suivantes peuvent également être utilisées pour limiter le nombre d'éléments sélectionnés resp. de fichiers
uploadés :
MinLength |
nombre minimum | int |
MaxLength |
nombre maximum | int |
Length |
nombre dans une plage ou nombre exact | paire [int, int] ou int |
Upload de fichiers
Pour les éléments addUpload()
, addMultiUpload()
, les règles suivantes peuvent également être
utilisées :
MaxFileSize |
taille maximale du fichier en octets | int |
MimeType |
type MIME, caractères génériques autorisés ('video/*' ) |
string|string[] |
Image |
image JPEG, PNG, GIF, WebP, AVIF | – |
Pattern |
le nom du fichier correspond à l'expression régulière | string |
PatternInsensitive |
comme Pattern , mais insensible à la casse |
string |
MimeType
et Image
nécessitent l'extension PHP fileinfo
. Le fait qu'un fichier ou une
image soit du type requis est détecté sur la base de sa signature et ne vérifie pas l'intégrité de l'ensemble du
fichier. On peut vérifier si une image n'est pas endommagée, par exemple, en essayant de la charger.
Messages d'erreur
Toutes les règles prédéfinies à l'exception de Pattern
et PatternInsensitive
ont un message
d'erreur par défaut, il est donc possible de l'omettre. Cependant, en indiquant et en formulant tous les messages sur mesure,
vous rendrez le formulaire plus convivial.
Vous pouvez modifier les messages par défaut dans la configuration,
en modifiant les textes dans le tableau Nette\Forms\Validator::$messages
ou en utilisant un traducteur.
Dans le texte des messages d'erreur, les chaînes de remplacement suivantes peuvent être utilisées :
%d |
remplace successivement par les arguments de la règle |
%n$d |
remplace par le n-ième argument de la règle |
%label |
remplace par le libellé de l'élément (sans les deux points) |
%name |
remplace par le nom de l'élément (par ex. name ) |
%value |
remplace par la valeur saisie par l'utilisateur |
$form->addText('name', 'Nom:')
->setRequired('Veuillez remplir %label');
$form->addInteger('id', 'ID:')
->addRule($form::Range, 'au moins %d et au plus %d', [5, 10]);
$form->addInteger('id', 'ID:')
->addRule($form::Range, 'au plus %2$d et au moins %1$d', [5, 10]);
Conditions
En plus des règles, il est également possible d'ajouter des conditions. Elles s'écrivent de la même manière que les
règles, sauf qu'au lieu de addRule()
, nous utilisons la méthode addCondition()
et, bien sûr, nous
n'indiquons aucun message d'erreur (la condition ne fait que demander) :
$form->addPassword('password', 'Mot de passe:')
// si le mot de passe n'est pas plus long que 8 caractères
->addCondition($form::MaxLength, 8)
// alors il doit contenir un chiffre
->addRule($form::Pattern, 'Doit contenir un chiffre', '.*[0-9].*');
La condition peut également être liée à un autre élément que l'élément actuel à l'aide de
addConditionOn()
. Comme premier paramètre, nous indiquons une référence à l'élément. Dans cet exemple, l'e-mail
ne sera obligatoire que si la case à cocher est cochée (sa valeur sera true) :
$form->addCheckbox('newsletters', 'envoyez-moi les newsletters');
$form->addEmail('email', 'E-mail:')
// si la case à cocher est cochée
->addConditionOn($form['newsletters'], $form::Equal, true)
// alors exiger l'e-mail
->setRequired('Saisissez une adresse e-mail');
Il est possible de créer des structures complexes à partir de conditions à l'aide de elseCondition()
et
endCondition()
:
$form->addText(/* ... */)
->addCondition(/* ... */) // si la première condition est remplie
->addConditionOn(/* ... */) // et la deuxième condition sur un autre élément
->addRule(/* ... */) // exiger cette règle
->elseCondition() // si la deuxième condition n'est pas remplie
->addRule(/* ... */) // exiger ces règles
->addRule(/* ... */)
->endCondition() // nous revenons à la première condition
->addRule(/* ... */);
Dans Nette, il est très facile de réagir à la satisfaction ou non d'une condition également côté JavaScript à l'aide de
la méthode toggle()
, voir javascript dynamique.
Référence à un autre élément
Comme argument de règle ou de condition, il est également possible de passer un autre élément du formulaire. La règle
utilisera alors la valeur saisie ultérieurement par l'utilisateur dans le navigateur. Ainsi, il est possible, par exemple, de
valider dynamiquement que l'élément password
contient la même chaîne que l'élément
password_confirm
:
$form->addPassword('password', 'Mot de passe');
$form->addPassword('password_confirm', 'Confirmez le mot de passe')
->addRule($form::Equal, 'Les mots de passe saisis ne correspondent pas', $form['password']);
Règles et conditions personnalisées
Parfois, nous nous trouvons dans une situation où les règles de validation intégrées dans Nette ne suffisent pas et nous devons valider les données de l'utilisateur à notre manière. Dans Nette, c'est très simple !
Aux méthodes addRule()
ou addCondition()
, il est possible de passer n'importe quel callback comme
premier paramètre. Celui-ci reçoit comme premier paramètre l'élément lui-même et retourne une valeur booléenne indiquant si
la validation s'est déroulée correctement. Lors de l'ajout d'une règle à l'aide de addRule()
, il est possible de
spécifier d'autres arguments, qui sont ensuite passés comme deuxième paramètre.
Nous pouvons ainsi créer notre propre ensemble de validateurs sous forme de classe avec des méthodes statiques :
class MyValidators
{
// teste si la valeur est divisible par l'argument
public static function validateDivisibility(Nette\Forms\Control $input, $arg): bool
{
return $input->getValue() % $arg === 0;
}
public static function validateEmailDomain(Nette\Forms\Control $input, $domain)
{
// autres validateurs
}
}
L'utilisation est alors très simple :
$form->addInteger('num')
->addRule(
[MyValidators::class, 'validateDivisibility'],
'La valeur doit être un multiple de %d',
8,
);
Il est également possible d'ajouter des règles de validation personnalisées à JavaScript. La condition est que la règle
soit une méthode statique. Son nom pour le validateur JavaScript est formé en joignant le nom de la classe sans les barres
obliques inverses \
, le trait de soulignement _
et le nom de la méthode. Par exemple,
App\MyValidators::validateDivisibility
s'écrira AppMyValidators_validateDivisibility
et sera ajouté à
l'objet Nette.validators
:
Nette.validators['AppMyValidators_validateDivisibility'] = (elem, args, val) => {
return val % args === 0;
};
Événement onValidate
Après la soumission du formulaire, une validation est effectuée, au cours de laquelle les règles individuelles ajoutées à
l'aide de addRule()
sont vérifiées, puis l'événement
onValidate
est déclenché. Son gestionnaire peut être utilisé pour une validation supplémentaire, typiquement
pour vérifier la combinaison correcte de valeurs dans plusieurs éléments du formulaire.
Si une erreur est détectée, nous la transmettons au formulaire à l'aide de la méthode addError()
. Celle-ci
peut être appelée soit sur un élément spécifique, soit directement sur le formulaire.
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('Cette combinaison n\'est pas possible.');
}
}
Erreurs lors du traitement
Dans de nombreux cas, nous ne découvrons une erreur qu'au moment où nous traitons un formulaire valide, par exemple lors de
l'écriture d'un nouvel élément dans la base de données et que nous rencontrons une duplication de clés. Dans ce cas, nous
transmettons à nouveau l'erreur au formulaire à l'aide de la méthode addError()
. Celle-ci peut être appelée soit
sur un élément spécifique, soit directement sur le formulaire :
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('Mot de passe invalide.');
}
}
Si possible, nous recommandons d'attacher l'erreur directement à l'élément du formulaire, car elle s'affichera à côté de lui lors de l'utilisation du moteur de rendu par défaut.
$form['date']->addError('Désolé, mais cette date est déjà prise.');
Vous pouvez appeler addError()
à plusieurs reprises et ainsi transmettre plusieurs messages d'erreur au
formulaire ou à l'élément. Vous les obtenez à l'aide de getErrors()
.
Attention, $form->getErrors()
retourne un résumé de tous les messages d'erreur, y compris ceux qui ont été
transmis directement aux éléments individuels, et pas seulement directement au formulaire. Les messages d'erreur transmis
uniquement au formulaire sont obtenus via $form->getOwnErrors()
.
Modification de l'entrée
À l'aide de la méthode addFilter()
, nous pouvons modifier la valeur saisie par l'utilisateur. Dans cet exemple,
nous tolérerons et supprimerons les espaces dans le code postal :
$form->addText('zip', 'Code postal:')
->addFilter(function ($value) {
return str_replace(' ', '', $value); // nous supprimons les espaces du code postal
})
->addRule($form::Pattern, 'Le code postal n\'est pas sous la forme de cinq chiffres', '\d{5}');
Le filtre s'insère entre les règles de validation et les conditions, et l'ordre des méthodes est donc important,
c'est-à-dire que le filtre et la règle sont appelés dans l'ordre où les méthodes addFilter()
et
addRule()
sont placées.
Validation JavaScript
Le langage pour formuler les conditions et les règles est très puissant. Toutes les constructions fonctionnent à la fois
côté serveur et côté JavaScript. Elles sont transmises dans les attributs HTML data-nette-rules
sous forme de
JSON. La validation elle-même est ensuite effectuée par un script qui intercepte l'événement submit
du
formulaire, parcourt les éléments individuels et effectue la validation appropriée.
Ce script est netteForms.js
et est disponible à partir de plusieurs sources possibles :
Vous pouvez insérer le script directement dans la page HTML depuis un CDN :
<script src="https://unpkg.com/nette-forms@3"></script>
Ou le copier localement dans le dossier public du projet (par ex. depuis
vendor/nette/forms/src/assets/netteForms.min.js
) :
<script src="/path/to/netteForms.min.js"></script>
Ou l'installer via npm :
npm install nette-forms
Et ensuite le charger et l'exécuter :
import netteForms from 'nette-forms';
netteForms.initOnLoad();
Alternativement, vous pouvez le charger directement depuis le dossier vendor
:
import netteForms from '../path/to/vendor/nette/forms/src/assets/netteForms.js';
netteForms.initOnLoad();
JavaScript dynamique
Voulez-vous afficher les champs pour saisir l'adresse uniquement si l'utilisateur choisit d'envoyer la marchandise par la poste
? Aucun problème. La clé est la paire de méthodes addCondition()
& toggle()
:
$form->addCheckbox('send_it')
->addCondition($form::Equal, true)
->toggle('#address-container');
Ce code dit que lorsque la condition est remplie, c'est-à-dire lorsque la case à cocher est cochée, l'élément HTML
#address-container
sera visible. Et inversement. Nous plaçons donc les éléments du formulaire avec l'adresse du
destinataire dans un conteneur avec cet ID, et lors du clic sur la case à cocher, ils se masquent ou s'affichent. C'est le script
netteForms.js
qui s'en charge.
Comme argument de la méthode toggle()
, il est possible de passer n'importe quel sélecteur. Pour des raisons
historiques, une chaîne alphanumérique sans autres caractères spéciaux est comprise comme l'ID de l'élément, c'est-à-dire
comme si elle était précédée du caractère #
. Le deuxième paramètre facultatif permet d'inverser le
comportement, c'est-à-dire que si nous utilisions toggle('#address-container', false)
, l'élément ne s'afficherait
au contraire que si la case à cocher n'était pas cochée.
L'implémentation par défaut en JavaScript modifie la propriété hidden
des éléments. Cependant, nous pouvons
facilement changer le comportement, par exemple en ajoutant une animation. Il suffit de remplacer la méthode
Nette.toggle
en JavaScript par notre propre solution :
Nette.toggle = (selector, visible, srcElement, event) => {
document.querySelectorAll(selector).forEach((el) => {
// nous masquons ou affichons 'el' selon la valeur de 'visible'
});
};
Désactivation de la validation
Parfois, il peut être utile de désactiver la validation. Si l'appui sur le bouton d'envoi ne doit pas effectuer de validation
(approprié pour les boutons Annuler ou Aperçu), nous la désactivons avec la méthode
$submit->setValidationScope([])
. S'il doit effectuer une validation partielle, nous pouvons spécifier quels
champs ou conteneurs de formulaire doivent être validés.
$form->addText('name')
->setRequired();
$details = $form->addContainer('details');
$details->addInteger('age')
->setRequired('age');
$details->addInteger('age2')
->setRequired('age2');
$form->addSubmit('send1'); // Valide tout le formulaire
$form->addSubmit('send2')
->setValidationScope([]); // Ne valide rien du tout
$form->addSubmit('send3')
->setValidationScope([$form['name']]); // Valide uniquement l'élément name
$form->addSubmit('send4')
->setValidationScope([$form['details']['age']]); // Valide uniquement l'élément age
$form->addSubmit('send5')
->setValidationScope([$form['details']]); // Valide le conteneur details
setValidationScope
n'affecte pas l'événement onValidate du formulaire,
qui sera toujours appelée. L'événement onValidate
du conteneur ne sera déclenché que si ce conteneur est marqué
pour une validation partielle.