Nette Documentation Preview

syntax
Validation des formulaires
**************************


Contrôles obligatoires .[#toc-required-controls]
================================================

Les contrôles sont marqués comme obligatoires par la méthode `setRequired()`, dont l'argument est le texte du [message d'erreur |#Error Messages] qui sera affiché si l'utilisateur ne le remplit pas. Si aucun argument n'est donné, le message d'erreur par défaut est utilisé.

```php
$form->addText('name', 'Name:')
	->setRequired('Please fill your name.');
```


Règles .[#toc-rules]
====================

Nous ajoutons des règles de validation aux contrôles avec la méthode `addRule()`. Le premier paramètre est la règle, le deuxième est le [message d'erreur |#Error Messages], et le troisième est l'argument de la règle de validation.

```php
$form->addPassword('password', 'Password:')
	->addRule($form::MinLength, 'Password must be at least %d characters', 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 appliquer ces règles à tous les éléments :

| constante | description | arguments
|-------
| `Required` | alias de `setRequired()` | -
| `Filled` | alias de `setRequired()` | -
| `Blank` | 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 à un élément du tableau | `array`
| `IsNotIn` | la valeur n'est égale à aucun élément du tableau | `array`
| `Valid` | L'entrée passe la validation (pour les [conditions |#conditions]) | -


Entrées de texte .[#toc-text-inputs]
------------------------------------

Pour les éléments `addText()`, `addPassword()`, `addTextArea()`, `addEmail()`, `addInteger()`, `addFloat()`, certaines des règles suivantes peuvent également être appliquées :

| `MinLength` | longueur minimale de la chaîne | `int`
| `MaxLength` | longueur maximale de la chaîne de caractères | `int`
| `Length` | longueur dans l'intervalle ou longueur exacte | paire `[int, int]` ou `int`
| `Email` | adresse électronique valide | -
| `URL` | URL valide | -
| `Pattern` | correspond à un modèle régulier | `string`
| `PatternInsensitive` | comme `Pattern`, mais insensible à la casse | `string`
| `Integer` | nombre entier | -
| `Numeric` | alias de `Integer` | -
| `Float` | nombre entier ou à virgule flottante | -
| `Min` | minimum de la valeur entière | `int\|float`
| `Max` | maximum de la valeur entière | `int\|float`
| `Range` | valeur dans l'intervalle | paire `[int\|float, int\|float]`

Les règles `Integer`, `Numeric` et `Float` convertissent automatiquement la valeur en nombre entier (ou flottant respectivement). En outre, la règle `URL` accepte également une adresse sans schéma (par exemple `nette.org`) et complète le schéma (`https://nette.org`).
Les expressions de `Pattern` et `PatternInsensitive` doivent être valables pour la valeur entière, c'est-à-dire comme si elle était enveloppée dans les caractères `^` and `$`.


Nombre d'articles .[#toc-number-of-items]
-----------------------------------------

Pour les éléments `addMultiUpload()`, `addCheckboxList()`, `addMultiSelect()` vous pouvez également utiliser les règles suivantes pour limiter le nombre d'éléments sélectionnés ou de fichiers téléchargés :

 `MinLength` | nombre minimum | | nombre maximum `int`
| `MaxLength` | nombre maximum `int`
| `Length` | nombre dans l'intervalle ou nombre exact | paires `[int, int]` ou `int`


Téléchargement de fichiers
--------------------------

Pour les contrôles `addUpload()`, `addMultiUpload()`, les règles suivantes peuvent également être utilisées :

| `MaxFileSize` | taille maximale du fichier en octets | `int`
`MimeType` | type MIME, accepte les caractères génériques (`'video/*'`) | type MIME, accepte les caractères génériques ( ) | type MIME, accepte les caractères génériques ( ) `string\|string[]`
| `Image` | le fichier téléchargé est un JPEG, PNG, GIF, WebP | -
| `Pattern` | le nom du fichier correspond à une expression régulière | `string`
| `PatternInsensitive` | comme `Pattern`, mais insensible à la casse | `string`

Les sites `MimeType` et `Image` nécessitent l'extension PHP `fileinfo`. Le fait qu'un fichier ou une image soit du type requis est détecté par sa signature. L'intégrité de l'ensemble du fichier n'est pas vérifiée. Vous pouvez savoir si une image n'est pas corrompue, par exemple en essayant de [la charger |http:request#toImage].


Messages d'erreur .[#toc-error-messages]
========================================

Toutes les règles prédéfinies, à l'exception de `Pattern` et `PatternInsensitive`, ont un message d'erreur par défaut ; elles peuvent donc être omises. Toutefois, en passant et en formulant tous les messages personnalisés, vous rendrez le formulaire plus convivial.

Vous pouvez changer les messages par défaut dans [forms:configuration], en modifiant les textes dans le tableau `Nette\Forms\Validator::$messages` ou en utilisant [translator |rendering#translating].

Les caractères de remplacement suivants peuvent être utilisés dans le texte des messages d'erreur :

| `%d` | remplace progressivement les règles après les arguments
| `%n$d` | remplace par le nième argument de règle
| `%label` | remplace par le libellé du champ (sans les deux points)
| `%name` | remplace par le nom du champ (par exemple `name`)
| `%value` | remplace par la valeur entrée par l'utilisateur

```php
$form->addText('name', 'Name:')
	->setRequired('Please fill in %label');

$form->addInteger('id', 'ID:')
	->addRule($form::Range, 'at least %d and no more than %d', [5, 10]);

$form->addInteger('id', 'ID:')
	->addRule($form::Range, 'no more than %2$d and at least %1$d', [5, 10]);
```


Conditions .[#toc-conditions]
=============================

Outre les règles de validation, il est possible de définir des conditions. Elles sont définies de la même manière que les règles, mais nous utilisons `addRule()` au lieu de `addCondition()` et, bien sûr, nous ne laissons pas de message d'erreur (la condition demande simplement) :

```php
$form->addPassword('password', 'Password:')
	// si le mot de passe ne dépasse pas 8 caractères ...
	->addCondition($form::MaxLength, 8)
		// ... alors il doit contenir un nombre
		->addRule($form::Pattern, 'Must contain number', '.*[0-9].*');
```

La condition peut être liée à un élément différent de l'élément actuel en utilisant `addConditionOn()`. Le premier paramètre est une référence au champ. Dans le cas suivant, l'e-mail ne sera requis que si la case est cochée (c'est-à-dire si sa valeur est `true`) :

```php
$form->addCheckbox('newsletters', 'send me newsletters');

$form->addEmail('email', 'Email:')
	// si la case est cochée ...
	->addConditionOn($form['newsletters'], $form::Equal, true)
		// ... nécessite un email
		->setRequired('Remplissez votre adresse e-mail');
```

Les conditions peuvent être regroupées en structures complexes avec les méthodes `elseCondition()` et `endCondition()`.

```php
$form->addText(/* ... */)
	->addCondition(/* ... */) // si la première condition est remplie
		->addConditionOn(/* ... */) // et la deuxième condition sur un autre élément aussi
			->addRule(/* ... */) // nécessite cette règle
		->elseCondition() // si la deuxième condition n'est pas remplie
			->addRule(/* ... */) // nécessite ces règles
			->addRule(/* ... */)
		->endCondition() // on revient à la première condition
		->addRule(/* ... */);
```

Dans Nette, il est très facile de réagir à la réalisation ou non d'une condition du côté JavaScript en utilisant la méthode `toggle()`, voir [Dynamic JavaScript |#Dynamic JavaScript].


Référence à un autre élément .[#toc-reference-to-another-element]
=================================================================

En tant qu'argument d'une règle ou d'une condition, vous pouvez également transmettre un autre élément de formulaire. La règle utilisera alors la valeur introduite ultérieurement par l'utilisateur dans le navigateur. Cela peut être utilisé, par exemple, pour valider dynamiquement que l'élément `password` contient la même chaîne de caractères que l'élément `password_confirm`:

```php
$form->addPassword('password', 'Password');
$form->addPassword('password_confirm', 'Confirm Password')
    ->addRule($form::Equal, 'The passwords do not match', $form['password']);
```


Règles et conditions personnalisées .[#toc-custom-rules-and-conditions]
=======================================================================

Parfois, nous nous trouvons dans une situation où les règles de validation intégrées dans Nette ne sont pas suffisantes et nous devons valider les données de l'utilisateur à notre manière. Avec Nette, c'est très facile !

Vous pouvez passer n'importe quel callback comme premier paramètre aux méthodes `addRule()` ou `addCondition()`. La callback accepte l'élément lui-même comme premier paramètre et renvoie une valeur booléenne indiquant si la validation a réussi. Lors de l'ajout d'une règle à l'aide de `addRule()`, il est possible de transmettre des arguments supplémentaires, qui sont ensuite transmis comme deuxième paramètre.

L'ensemble personnalisé de validateurs peut donc être créé comme une classe avec des méthodes statiques :

```php
class MyValidators
{
	// teste si la valeur est divisible par l'argument
	public static function validateDivisibility(BaseControl $input, $arg): bool
	{
		return $input->getValue() % $arg === 0;
	}

	public static function validateEmailDomain(BaseControl $input, $domain)
	{
		// validateurs supplémentaires
	}
}
```

L'utilisation est alors très simple :

```php
$form->addInteger('num')
	->addRule(
		[MyValidators::class, 'validateDivisibility'],
		'The value must be a multiple of %d',
		8,
	);
```

Les règles de validation personnalisées peuvent également être ajoutées à JavaScript. La seule exigence est que la règle doit être une méthode statique. Son nom pour le validateur JavaScript est créé en concaténant le nom de la classe sans les barres obliques inversées `\`, the underscore `_`, et le nom de la méthode. Par exemple, écrivez `App\MyValidators::validateDivisibility` sous la forme `AppMyValidators_validateDivisibility` et ajoutez-la à l'objet `Nette.validators`:

```js
Nette.validators['AppMyValidators_validateDivisibility'] = (elem, args, val) => {
	return val % args === 0;
};
```


Événement onValidate .[#toc-event-onvalidate]
=============================================

Après l'envoi du formulaire, la validation est effectuée en vérifiant les règles individuelles ajoutées par `addRule()`, puis en appelant l'[événement |nette:glossary#Events] `onValidate`. Son gestionnaire peut être utilisé pour une validation supplémentaire, généralement pour vérifier la combinaison correcte de valeurs dans plusieurs éléments du formulaire.

Si une erreur est détectée, elle est transmise 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.

```php
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 de traitement .[#toc-processing-errors]
===============================================

Dans de nombreux cas, nous découvrons une erreur lors du traitement d'un formulaire valide, par exemple lorsque nous écrivons une nouvelle entrée dans la base de données et que nous rencontrons un doublon de clé. Dans ce cas, nous transmettons l'erreur au formulaire à l'aide de la méthode `addError()`. Cette méthode peut être appelée soit sur un élément spécifique, soit directement sur le formulaire :

```php
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 possible, nous recommandons d'ajouter l'erreur directement à l'élément de formulaire, car elle apparaîtra à côté de celui-ci lors de l'utilisation du moteur de rendu par défaut.

```php
$form['date']->addError('Sorry, this date is already taken.');
```

Vous pouvez appeler `addError()` à plusieurs reprises pour transmettre plusieurs messages d'erreur à un formulaire ou à un élément. Vous les obtenez avec `getErrors()`.

Notez que `$form->getErrors()` renvoie un résumé de tous les messages d'erreur, même ceux transmis directement aux éléments individuels, et pas seulement ceux transmis directement au formulaire. Les messages d'erreur passés uniquement au formulaire sont récupérés via `$form->getOwnErrors()`.


Modification des valeurs d'entrée .[#toc-modifying-input-values]
================================================================

En utilisant la méthode `addFilter()`, nous pouvons modifier la valeur saisie par l'utilisateur. Dans cet exemple, nous allons tolérer et supprimer les espaces dans le code postal :

```php
$form->addText('zip', 'Postcode:')
	->addFilter(function ($value) {
		return str_replace(' ', '', $value); // supprime les espaces du code postal
	})
	->addRule($form::Pattern, 'Le code postal n'est pas composé de cinq chiffres', '\d{5}');
```

Le filtre est inclus entre les règles et les conditions de validation et dépend donc de l'ordre des méthodes, c'est-à-dire que le filtre et la règle sont appelés dans le même ordre que celui des méthodes `addFilter()` et `addRule()`.


Validation JavaScript .[#toc-javascript-validation]
===================================================

Le langage des règles et conditions de validation est puissant. Même si toutes les constructions fonctionnent aussi bien du côté serveur que du côté client, en JavaScript. Les règles sont transférées dans des attributs HTML `data-nette-rules` sous forme de JSON.
La validation elle-même est gérée par un autre script, qui s'accroche à tous les événements du formulaire `submit`, itère sur toutes les entrées et exécute les validations respectives.

Ce script est `netteForms.js`, qui est disponible à partir de plusieurs sources possibles :

Vous pouvez intégrer le script directement dans la page HTML à partir du CDN :

```latte
<script src="https://unpkg.com/nette-forms@3"></script>
```

Ou le copier localement dans le dossier public du projet (par exemple à partir de `vendor/nette/forms/src/assets/netteForms.min.js`) :

```latte
<script src="/path/to/netteForms.min.js"></script>
```

Ou installer via [npm |https://www.npmjs.com/package/nette-forms]:

```shell
npm install nette-forms
```

Et ensuite charger et exécuter :

```js
import netteForms from 'nette-forms';
netteForms.initOnLoad();
```

Sinon, vous pouvez le charger directement depuis le dossier `vendor`:

```js
import netteForms from '../path/to/vendor/nette/forms/src/assets/netteForms.js';
netteForms.initOnLoad();
```


JavaScript dynamique .[#toc-dynamic-javascript]
===============================================

Vous souhaitez afficher les champs d'adresse uniquement si l'utilisateur choisit d'envoyer les marchandises par la poste ? Aucun problème. La clé est une paire de méthodes `addCondition()` & `toggle()`:

```php
$form->addCheckbox('send_it')
	->addCondition($form::Equal, true)
		->toggle('#address-container');
```

Ce code indique que lorsque la condition est remplie, c'est-à-dire lorsque la case est cochée, l'élément HTML `#address-container` sera visible. Et vice versa. Ainsi, nous plaçons les éléments de formulaire contenant l'adresse du destinataire dans un conteneur avec cet ID, et lorsque la case à cocher est activée, ils sont cachés ou affichés. Cette opération est gérée par le script `netteForms.js`.

Tout sélecteur peut être transmis comme argument à la méthode `toggle()`. Pour des raisons historiques, une chaîne alphanumérique ne comportant aucun autre caractère spécial est traitée comme un identifiant d'élément, comme si elle était précédée de l'adresse `#` character. The second optional parameter allows us to reverse the behavior, i.e. if we used `toggle('#address-container', false)`. L'élément ne serait affiché que si la case à cocher n'était pas cochée.

L'implémentation JavaScript par défaut modifie la propriété `hidden` pour les éléments. Toutefois, nous pouvons facilement modifier le comportement, par exemple en ajoutant une animation. Il suffit de remplacer la méthode `Nette.toggle` en JavaScript par une solution personnalisée :

```js
Nette.toggle = (selector, visible, srcElement, event) => {
	document.querySelectorAll(selector).forEach((el) => {
		// hide or show 'el' according to the value of 'visible'
	});
};
```


Désactiver la validation .[#toc-disabling-validation]
=====================================================

Dans certains cas, vous devez désactiver la validation. Si un bouton d'envoi n'est pas censé exécuter la validation après l'envoi (par exemple le bouton *Annulation* ou *Aperçu*), vous pouvez désactiver la validation en appelant `$submit->setValidationScope([])`. Vous pouvez également valider partiellement le formulaire en spécifiant les éléments à valider.

```php
$form->addText('name')
	->setRequired();

$details = $form->addContainer('details');
$details->addInteger('age')
	->setRequired('age');
$details->addInteger('age2')
	->setRequired('age2');

$form->addSubmit('send1'); // Valide l'ensemble du formulaire
$form->addSubmit('send2')
	->setValidationScope([]); // Ne valide rien.
$form->addSubmit('send3')
	->setValidationScope([$form['name']]); // Ne valide que le champ 'name'.
$form->addSubmit('send4')
	->setValidationScope([$form['details']['age']]); // Valide uniquement le champ 'age'.
$form->addSubmit('send5')
	->setValidationScope([$form['details']]); // Valide le conteneur 'details'.
```

L'[événement onValidate |#Event onValidate] sur le formulaire est toujours invoqué et n'est pas affecté par l'appel `setValidationScope`. L'événement `onValidate` sur le conteneur est invoqué uniquement lorsque ce conteneur est spécifié pour la validation partielle.

Validation des formulaires

Contrôles obligatoires

Les contrôles sont marqués comme obligatoires par la méthode setRequired(), dont l'argument est le texte du message d'erreur qui sera affiché si l'utilisateur ne le remplit pas. Si aucun argument n'est donné, le message d'erreur par défaut est utilisé.

$form->addText('name', 'Name:')
	->setRequired('Please fill your name.');

Règles

Nous ajoutons des règles de validation aux contrôles avec la méthode addRule(). Le premier paramètre est la règle, le deuxième est le message d'erreur, et le troisième est l'argument de la règle de validation.

$form->addPassword('password', 'Password:')
	->addRule($form::MinLength, 'Password must be at least %d characters', 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 appliquer ces règles à tous les éléments :

constante description arguments
Required alias de setRequired()
Filled alias de setRequired()
Blank 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 à un élément du tableau array
IsNotIn la valeur n'est égale à aucun élément du tableau array
Valid L'entrée passe la validation (pour les conditions)

Entrées de texte

Pour les éléments addText(), addPassword(), addTextArea(), addEmail(), addInteger(), addFloat(), certaines des règles suivantes peuvent également être appliquées :

MinLength longueur minimale de la chaîne int
MaxLength longueur maximale de la chaîne de caractères int
Length longueur dans l'intervalle ou longueur exacte paire [int, int] ou int
Email adresse électronique valide
URL URL valide
Pattern correspond à un modèle régulier string
PatternInsensitive comme Pattern, mais insensible à la casse string
Integer nombre entier
Numeric alias de Integer
Float nombre entier ou à virgule flottante
Min minimum de la valeur entière int|float
Max maximum de la valeur entière int|float
Range valeur dans l'intervalle paire [int|float, int|float]

Les règles Integer, Numeric et Float convertissent automatiquement la valeur en nombre entier (ou flottant respectivement). En outre, la règle URL accepte également une adresse sans schéma (par exemple nette.org) et complète le schéma (https://nette.org). Les expressions de Pattern et PatternInsensitive doivent être valables pour la valeur entière, c'est-à-dire comme si elle était enveloppée dans les caractères ^ and $.

Nombre d'articles

Pour les éléments addMultiUpload(), addCheckboxList(), addMultiSelect() vous pouvez également utiliser les règles suivantes pour limiter le nombre d'éléments sélectionnés ou de fichiers téléchargés :

MinLength | nombre minimum | | nombre maximum int

MaxLength nombre maximum int
Length nombre dans l'intervalle ou nombre exact paires [int, int] ou int

Téléchargement de fichiers

Pour les contrôles addUpload(), addMultiUpload(), les règles suivantes peuvent également être utilisées :

MaxFileSize taille maximale du fichier en octets int

MimeType | type MIME, accepte les caractères génériques ('video/*') | type MIME, accepte les caractères génériques ( ) | type MIME, accepte les caractères génériques ( ) string\|string[]

Image le fichier téléchargé est un JPEG, PNG, GIF, WebP
Pattern le nom du fichier correspond à une expression régulière string
PatternInsensitive comme Pattern, mais insensible à la casse string

Les sites MimeType et Image nécessitent l'extension PHP fileinfo. Le fait qu'un fichier ou une image soit du type requis est détecté par sa signature. L'intégrité de l'ensemble du fichier n'est pas vérifiée. Vous pouvez savoir si une image n'est pas corrompue, 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 ; elles peuvent donc être omises. Toutefois, en passant et en formulant tous les messages personnalisés, vous rendrez le formulaire plus convivial.

Vous pouvez changer les messages par défaut dans configuration, en modifiant les textes dans le tableau Nette\Forms\Validator::$messages ou en utilisant translator.

Les caractères de remplacement suivants peuvent être utilisés dans le texte des messages d'erreur :

%d remplace progressivement les règles après les arguments
%n$d remplace par le nième argument de règle
%label remplace par le libellé du champ (sans les deux points)
%name remplace par le nom du champ (par exemple name)
%value remplace par la valeur entrée par l'utilisateur
$form->addText('name', 'Name:')
	->setRequired('Please fill in %label');

$form->addInteger('id', 'ID:')
	->addRule($form::Range, 'at least %d and no more than %d', [5, 10]);

$form->addInteger('id', 'ID:')
	->addRule($form::Range, 'no more than %2$d and at least %1$d', [5, 10]);

Conditions

Outre les règles de validation, il est possible de définir des conditions. Elles sont définies de la même manière que les règles, mais nous utilisons addRule() au lieu de addCondition() et, bien sûr, nous ne laissons pas de message d'erreur (la condition demande simplement) :

$form->addPassword('password', 'Password:')
	// si le mot de passe ne dépasse pas 8 caractères ...
	->addCondition($form::MaxLength, 8)
		// ... alors il doit contenir un nombre
		->addRule($form::Pattern, 'Must contain number', '.*[0-9].*');

La condition peut être liée à un élément différent de l'élément actuel en utilisant addConditionOn(). Le premier paramètre est une référence au champ. Dans le cas suivant, l'e-mail ne sera requis que si la case est cochée (c'est-à-dire si sa valeur est true) :

$form->addCheckbox('newsletters', 'send me newsletters');

$form->addEmail('email', 'Email:')
	// si la case est cochée ...
	->addConditionOn($form['newsletters'], $form::Equal, true)
		// ... nécessite un email
		->setRequired('Remplissez votre adresse e-mail');

Les conditions peuvent être regroupées en structures complexes avec les méthodes elseCondition() et endCondition().

$form->addText(/* ... */)
	->addCondition(/* ... */) // si la première condition est remplie
		->addConditionOn(/* ... */) // et la deuxième condition sur un autre élément aussi
			->addRule(/* ... */) // nécessite cette règle
		->elseCondition() // si la deuxième condition n'est pas remplie
			->addRule(/* ... */) // nécessite ces règles
			->addRule(/* ... */)
		->endCondition() // on revient à la première condition
		->addRule(/* ... */);

Dans Nette, il est très facile de réagir à la réalisation ou non d'une condition du côté JavaScript en utilisant la méthode toggle(), voir Dynamic JavaScript.

Référence à un autre élément

En tant qu'argument d'une règle ou d'une condition, vous pouvez également transmettre un autre élément de formulaire. La règle utilisera alors la valeur introduite ultérieurement par l'utilisateur dans le navigateur. Cela peut être utilisé, par exemple, pour valider dynamiquement que l'élément password contient la même chaîne de caractères que l'élément password_confirm:

$form->addPassword('password', 'Password');
$form->addPassword('password_confirm', 'Confirm Password')
    ->addRule($form::Equal, 'The passwords do not match', $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 sont pas suffisantes et nous devons valider les données de l'utilisateur à notre manière. Avec Nette, c'est très facile !

Vous pouvez passer n'importe quel callback comme premier paramètre aux méthodes addRule() ou addCondition(). La callback accepte l'élément lui-même comme premier paramètre et renvoie une valeur booléenne indiquant si la validation a réussi. Lors de l'ajout d'une règle à l'aide de addRule(), il est possible de transmettre des arguments supplémentaires, qui sont ensuite transmis comme deuxième paramètre.

L'ensemble personnalisé de validateurs peut donc être créé comme une classe avec des méthodes statiques :

class MyValidators
{
	// teste si la valeur est divisible par l'argument
	public static function validateDivisibility(BaseControl $input, $arg): bool
	{
		return $input->getValue() % $arg === 0;
	}

	public static function validateEmailDomain(BaseControl $input, $domain)
	{
		// validateurs supplémentaires
	}
}

L'utilisation est alors très simple :

$form->addInteger('num')
	->addRule(
		[MyValidators::class, 'validateDivisibility'],
		'The value must be a multiple of %d',
		8,
	);

Les règles de validation personnalisées peuvent également être ajoutées à JavaScript. La seule exigence est que la règle doit être une méthode statique. Son nom pour le validateur JavaScript est créé en concaténant le nom de la classe sans les barres obliques inversées \, the underscore _, et le nom de la méthode. Par exemple, écrivez App\MyValidators::validateDivisibility sous la forme AppMyValidators_validateDivisibility et ajoutez-la à l'objet Nette.validators:

Nette.validators['AppMyValidators_validateDivisibility'] = (elem, args, val) => {
	return val % args === 0;
};

Événement onValidate

Après l'envoi du formulaire, la validation est effectuée en vérifiant les règles individuelles ajoutées par addRule(), puis en appelant l'événement onValidate. Son gestionnaire peut être utilisé pour une validation supplémentaire, généralement pour vérifier la combinaison correcte de valeurs dans plusieurs éléments du formulaire.

Si une erreur est détectée, elle est transmise 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 de traitement

Dans de nombreux cas, nous découvrons une erreur lors du traitement d'un formulaire valide, par exemple lorsque nous écrivons une nouvelle entrée dans la base de données et que nous rencontrons un doublon de clé. Dans ce cas, nous transmettons l'erreur au formulaire à l'aide de la méthode addError(). Cette méthode 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('Invalid password.');
	}
}

Si possible, nous recommandons d'ajouter l'erreur directement à l'élément de formulaire, car elle apparaîtra à côté de celui-ci lors de l'utilisation du moteur de rendu par défaut.

$form['date']->addError('Sorry, this date is already taken.');

Vous pouvez appeler addError() à plusieurs reprises pour transmettre plusieurs messages d'erreur à un formulaire ou à un élément. Vous les obtenez avec getErrors().

Notez que $form->getErrors() renvoie un résumé de tous les messages d'erreur, même ceux transmis directement aux éléments individuels, et pas seulement ceux transmis directement au formulaire. Les messages d'erreur passés uniquement au formulaire sont récupérés via $form->getOwnErrors().

Modification des valeurs d'entrée

En utilisant la méthode addFilter(), nous pouvons modifier la valeur saisie par l'utilisateur. Dans cet exemple, nous allons tolérer et supprimer les espaces dans le code postal :

$form->addText('zip', 'Postcode:')
	->addFilter(function ($value) {
		return str_replace(' ', '', $value); // supprime les espaces du code postal
	})
	->addRule($form::Pattern, 'Le code postal n'est pas composé de cinq chiffres', '\d{5}');

Le filtre est inclus entre les règles et les conditions de validation et dépend donc de l'ordre des méthodes, c'est-à-dire que le filtre et la règle sont appelés dans le même ordre que celui des méthodes addFilter() et addRule().

Validation JavaScript

Le langage des règles et conditions de validation est puissant. Même si toutes les constructions fonctionnent aussi bien du côté serveur que du côté client, en JavaScript. Les règles sont transférées dans des attributs HTML data-nette-rules sous forme de JSON. La validation elle-même est gérée par un autre script, qui s'accroche à tous les événements du formulaire submit, itère sur toutes les entrées et exécute les validations respectives.

Ce script est netteForms.js, qui est disponible à partir de plusieurs sources possibles :

Vous pouvez intégrer le script directement dans la page HTML à partir du CDN :

<script src="https://unpkg.com/nette-forms@3"></script>

Ou le copier localement dans le dossier public du projet (par exemple à partir de vendor/nette/forms/src/assets/netteForms.min.js) :

<script src="/path/to/netteForms.min.js"></script>

Ou installer via npm:

npm install nette-forms

Et ensuite charger et exécuter :

import netteForms from 'nette-forms';
netteForms.initOnLoad();

Sinon, 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

Vous souhaitez afficher les champs d'adresse uniquement si l'utilisateur choisit d'envoyer les marchandises par la poste ? Aucun problème. La clé est une paire de méthodes addCondition() & toggle():

$form->addCheckbox('send_it')
	->addCondition($form::Equal, true)
		->toggle('#address-container');

Ce code indique que lorsque la condition est remplie, c'est-à-dire lorsque la case est cochée, l'élément HTML #address-container sera visible. Et vice versa. Ainsi, nous plaçons les éléments de formulaire contenant l'adresse du destinataire dans un conteneur avec cet ID, et lorsque la case à cocher est activée, ils sont cachés ou affichés. Cette opération est gérée par le script netteForms.js.

Tout sélecteur peut être transmis comme argument à la méthode toggle(). Pour des raisons historiques, une chaîne alphanumérique ne comportant aucun autre caractère spécial est traitée comme un identifiant d'élément, comme si elle était précédée de l'adresse # character. The second optional parameter allows us to reverse the behavior, i.e. if we used toggle('#address-container', false). L'élément ne serait affiché que si la case à cocher n'était pas cochée.

L'implémentation JavaScript par défaut modifie la propriété hidden pour les éléments. Toutefois, nous pouvons facilement modifier le comportement, par exemple en ajoutant une animation. Il suffit de remplacer la méthode Nette.toggle en JavaScript par une solution personnalisée :

Nette.toggle = (selector, visible, srcElement, event) => {
	document.querySelectorAll(selector).forEach((el) => {
		// hide or show 'el' according to the value of 'visible'
	});
};

Désactiver la validation

Dans certains cas, vous devez désactiver la validation. Si un bouton d'envoi n'est pas censé exécuter la validation après l'envoi (par exemple le bouton Annulation ou Aperçu), vous pouvez désactiver la validation en appelant $submit->setValidationScope([]). Vous pouvez également valider partiellement le formulaire en spécifiant les éléments à valider.

$form->addText('name')
	->setRequired();

$details = $form->addContainer('details');
$details->addInteger('age')
	->setRequired('age');
$details->addInteger('age2')
	->setRequired('age2');

$form->addSubmit('send1'); // Valide l'ensemble du formulaire
$form->addSubmit('send2')
	->setValidationScope([]); // Ne valide rien.
$form->addSubmit('send3')
	->setValidationScope([$form['name']]); // Ne valide que le champ 'name'.
$form->addSubmit('send4')
	->setValidationScope([$form['details']['age']]); // Valide uniquement le champ 'age'.
$form->addSubmit('send5')
	->setValidationScope([$form['details']]); // Valide le conteneur 'details'.

L'événement onValidate sur le formulaire est toujours invoqué et n'est pas affecté par l'appel setValidationScope. L'événement onValidate sur le conteneur est invoqué uniquement lorsque ce conteneur est spécifié pour la validation partielle.