Formulaires utilisés indépendamment
Nette Forms facilite grandement la création et le traitement des formulaires web. Vous pouvez les utiliser dans vos applications de manière totalement indépendante du reste du framework, ce que nous allons montrer dans ce chapitre.
Cependant, si vous utilisez Nette Application et les presenters, le guide pour l'utilisation dans les presenters est fait pour vous.
Premier formulaire
Essayons d'écrire un formulaire d'inscription simple. Son code sera le suivant (code complet) :
use Nette\Forms\Form;
$form = new Form;
$form->addText('name', 'Nom:');
$form->addPassword('password', 'Mot de passe:');
$form->addSubmit('send', 'S\'inscrire');
Nous pouvons le rendre très facilement :
$form->render();
et dans le navigateur, il s'affichera comme ceci :

Le formulaire est un objet de la classe Nette\Forms\Form
(la classe Nette\Application\UI\Form
est
utilisée dans les presenters). Nous y avons ajouté ce qu'on appelle les éléments nom, mot de passe et un bouton d'envoi.
Maintenant, animons le formulaire. En interrogeant $form->isSuccess()
, nous vérifions si le formulaire a été
soumis et s'il a été rempli de manière valide. Si oui, nous affichons les données. Ajoutons donc après la définition du
formulaire :
if ($form->isSuccess()) {
echo 'Le formulaire a été correctement rempli et soumis';
$data = $form->getValues();
// $data->name contient le nom
// $data->password contient le mot de passe
var_dump($data);
}
La méthode getValues()
retourne les données soumises sous forme d'objet ArrayHash. Nous verrons plus
tard comment changer cela. L'objet $data
contient les clés name
et password
avec les
informations saisies par l'utilisateur.
Habituellement, nous envoyons directement les données pour un traitement ultérieur, qui peut être par exemple une insertion
dans la base de données. Cependant, une erreur peut survenir pendant le traitement, par exemple, le nom d'utilisateur est déjà
pris. Dans ce cas, nous transmettons l'erreur au formulaire à l'aide de addError()
et le laissons se rendre à
nouveau, avec le message d'erreur.
$form->addError('Désolé, ce nom d\'utilisateur est déjà utilisé.');
Après le traitement du formulaire, nous redirigeons vers la page suivante. Cela évite la soumission répétée involontaire du formulaire par le bouton actualiser, retour ou par le déplacement dans l'historique du navigateur.
Le formulaire est envoyé par défaut par la méthode POST et vers la même page. Les deux peuvent être modifiés :
$form->setAction('/submit.php');
$form->setMethod('GET');
Et c'est à peu près tout :-) Nous avons un formulaire fonctionnel et parfaitement sécurisé.
Essayez d'ajouter d'autres éléments de formulaire.
Accès aux éléments
Nous appelons le formulaire et ses éléments individuels des composants. Ils forment un arbre de composants, dont la racine est précisément le formulaire. Nous pouvons accéder aux éléments individuels du formulaire de cette manière :
$input = $form->getComponent('name');
// syntaxe alternative : $input = $form['name'];
$button = $form->getComponent('send');
// syntaxe alternative : $button = $form['send'];
Les éléments sont supprimés à l'aide de unset :
unset($form['name']);
Règles de validation
Nous avons mentionné le mot valide, mais le formulaire n'a pour l'instant aucune règle de validation. Corrigeons cela.
Le nom sera obligatoire, nous le marquerons donc avec la méthode setRequired()
, dont l'argument est le texte du
message d'erreur qui s'affichera si l'utilisateur ne remplit pas le nom. Si nous n'indiquons pas d'argument, le message d'erreur
par défaut sera utilisé.
$form->addText('name', 'Nom:')
->setRequired('Veuillez saisir un nom');
Essayez de soumettre le formulaire sans remplir le nom et vous verrez que le message d'erreur s'affichera et que le navigateur ou le serveur le refusera jusqu'à ce que vous remplissiez le champ.
En même temps, vous ne tromperez pas le système en tapant par exemple uniquement des espaces dans le champ. Non. Nette supprime automatiquement les espaces de début et de fin. Essayez-le. C'est quelque chose que vous devriez toujours faire avec chaque input sur une seule ligne, mais on l'oublie souvent. Nette le fait automatiquement. (Vous pouvez essayer de tromper le formulaire et envoyer une chaîne de caractères multiligne comme nom. Même ici, Nette ne se laisse pas berner et transforme les retours à la ligne en espaces.)
Le formulaire est toujours validé côté serveur, mais une validation JavaScript est également générée, qui s'exécute
instantanément et l'utilisateur est informé de l'erreur immédiatement, sans avoir besoin d'envoyer le formulaire au serveur.
C'est le script netteForms.js
qui s'en charge. Insérez-le dans la page :
<script src="https://unpkg.com/nette-forms@3"></script>
Si vous regardez le code source de la page avec le formulaire, vous remarquerez peut-être que Nette insère les éléments
obligatoires dans des éléments avec la classe CSS required
. Essayez d'ajouter la feuille de style suivante au
template et le libellé “Nom” sera rouge. Nous marquons ainsi élégamment les éléments obligatoires pour les
utilisateurs :
<style>
.required label { color: maroon }
</style>
Nous ajoutons d'autres règles de validation avec la méthode addRule()
. Le premier paramètre est la règle, le
deuxième est à nouveau le texte du message d'erreur et il peut encore suivre un argument de la règle de validation. Qu'est-ce
que cela signifie ?
Nous étendons le formulaire avec un nouveau champ facultatif “âge”, qui doit être un entier (addInteger()
)
et en plus dans une plage autorisée ($form::Range
). Et c'est ici que nous utilisons le troisième paramètre de la
méthode addRule()
, par lequel nous passons au validateur la plage requise sous forme de paire
[de, à]
:
$form->addInteger('age', 'Âge:')
->addRule($form::Range, 'L\'âge doit être compris entre 18 et 120 ans', [18, 120]);
Si l'utilisateur ne remplit pas le champ, les règles de validation ne seront pas vérifiées, car l'élément est facultatif.
Ici, il y a de la place pour un petit refactoring. Dans le message d'erreur et dans le troisième paramètre, les chiffres sont
indiqués en double, ce qui n'est pas idéal. Si nous créions des formulaires multilingues et que le message contenant des
chiffres était traduit dans plusieurs langues, un éventuel changement de valeurs serait compliqué. Pour cette raison, il est
possible d'utiliser les placeholders %d
et Nette complétera les valeurs :
->addRule($form::Range, 'L\'âge doit être compris entre %d et %d ans', [18, 120]);
Revenons à l'élément password
, que nous rendrons également obligatoire et vérifierons en plus la longueur
minimale du mot de passe ($form::MinLength
), à nouveau en utilisant le placeholder :
$form->addPassword('password', 'Mot de passe:')
->setRequired('Choisissez un mot de passe')
->addRule($form::MinLength, 'Le mot de passe doit comporter au moins %d caractères', 8);
Ajoutons au formulaire un champ passwordVerify
, où l'utilisateur saisira à nouveau le mot de passe, pour
vérification. À l'aide des règles de validation, nous vérifions si les deux mots de passe sont identiques
($form::Equal
). Et comme paramètre, nous donnons une référence au premier mot de passe en utilisant des crochets :
$form->addPassword('passwordVerify', 'Mot de passe pour vérification:')
->setRequired('Veuillez saisir à nouveau le mot de passe pour vérification')
->addRule($form::Equal, 'Les mots de passe ne correspondent pas', $form['password'])
->setOmitted();
Avec setOmitted()
, nous avons marqué l'élément dont la valeur ne nous importe pas vraiment et qui n'existe que
pour des raisons de validation. La valeur ne sera pas transmise à $data
.
Nous avons ainsi un formulaire entièrement fonctionnel avec validation en PHP et JavaScript. Les capacités de validation de Nette sont beaucoup plus larges, il est possible de créer des conditions, de faire afficher et masquer des parties de la page en fonction d'elles, etc. Vous apprendrez tout cela dans le chapitre sur la validation des formulaires.
Valeurs par défaut
Nous définissons couramment des valeurs par défaut pour les éléments du formulaire :
$form->addEmail('email', 'E-mail')
->setDefaultValue($lastUsedEmail);
Il est souvent utile de définir les valeurs par défaut pour tous les éléments en même temps. Par exemple, lorsque le formulaire sert à modifier des enregistrements. Nous lisons l'enregistrement de la base de données et définissons les valeurs par défaut :
//$row = ['name' => 'John', 'age' => '33', /* ... */];
$form->setDefaults($row);
Appelez setDefaults()
après la définition des éléments.
Rendu du formulaire
Par défaut, le formulaire est rendu sous forme de tableau. Les éléments individuels respectent la règle d'accessibilité de
base – tous les libellés sont écrits en tant que <label>
et liés à l'élément de formulaire
correspondant. En cliquant sur le libellé, le curseur apparaît automatiquement dans le champ du formulaire.
Nous pouvons définir des attributs HTML arbitraires pour chaque élément. Par exemple, ajouter un placeholder :
$form->addInteger('age', 'Âge:')
->setHtmlAttribute('placeholder', 'Veuillez remplir l\'âge');
Il existe vraiment un grand nombre de façons de rendre un formulaire, c'est pourquoi un chapitre séparé sur le rendu y est consacré.
Mappage sur des classes
Revenons au traitement des données du formulaire. La méthode getValues()
nous retournait les données soumises
sous forme d'objet ArrayHash
. Comme il s'agit d'une classe générique, quelque chose comme stdClass
, il
nous manquera un certain confort lors de son utilisation, comme l'autocomplétion des propriétés dans les éditeurs ou l'analyse
statique du code. Cela pourrait être résolu en ayant une classe spécifique pour chaque formulaire, dont les propriétés
représentent les éléments individuels. Par exemple :
class RegistrationFormData
{
public string $name;
public ?int $age;
public string $password;
}
Alternativement, vous pouvez utiliser un constructeur :
class RegistrationFormData
{
public function __construct(
public string $name,
public int $age,
public string $password,
) {
}
}
Les propriétés de la classe de données peuvent également être des enums et leur mappage se fera automatiquement.
Comment dire à Nette de nous retourner les données sous forme d'objets de cette classe ? Plus facilement que vous ne le pensez. Il suffit d'indiquer le nom de la classe ou l'objet à hydrater comme paramètre :
$data = $form->getValues(RegistrationFormData::class);
$name = $data->name;
Il est également possible d'indiquer 'array'
comme paramètre, et les données seront alors retournées sous
forme de tableau.
Si les formulaires forment une structure à plusieurs niveaux composée de conteneurs, créez une classe distincte pour chacun :
$form = new Form;
$person = $form->addContainer('person');
$person->addText('firstName');
/* ... */
class PersonFormData
{
public string $firstName;
public string $lastName;
}
class RegistrationFormData
{
public PersonFormData $person;
public ?int $age;
public string $password;
}
Le mappage reconnaîtra alors à partir du type de la propriété $person
qu'il doit mapper le conteneur sur la
classe PersonFormData
. Si la propriété contenait un tableau de conteneurs, indiquez le type array
et
passez la classe pour le mappage directement au conteneur :
$person->setMappedType(PersonFormData::class);
Vous pouvez faire générer la conception de la classe de données du formulaire à l'aide de la méthode
Nette\Forms\Blueprint::dataClass($form)
, qui l'affichera dans la page du navigateur. Il suffit ensuite de cliquer
pour sélectionner le code et de le copier dans votre projet.
Plusieurs boutons
Si le formulaire a plus d'un bouton, nous avons généralement besoin de distinguer lequel d'entre eux a été pressé. Cette
information nous est retournée par la méthode isSubmittedBy()
du bouton :
$form->addSubmit('save', 'Enregistrer');
$form->addSubmit('delete', 'Supprimer');
if ($form->isSuccess()) {
if ($form['save']->isSubmittedBy()) {
// ...
}
if ($form['delete']->isSubmittedBy()) {
// ...
}
}
Ne sautez pas la requête $form->isSuccess()
, elle vérifie la validité des données.
Lorsque le formulaire est soumis avec la touche Entrée, cela est considéré comme s'il avait été soumis par le premier bouton.
Protection contre les vulnérabilités
Nette Framework accorde une grande importance à la sécurité et veille donc scrupuleusement à la bonne sécurisation des formulaires.
En plus de protéger les formulaires contre les attaques Cross Site Scripting (XSS) et Cross-Site Request Forgery (CSRF), il effectue de nombreuses petites sécurisations auxquelles vous n'avez plus besoin de penser.
Par exemple, il filtre tous les caractères de contrôle des entrées et vérifie la validité de l'encodage UTF-8, de sorte que les données du formulaire seront toujours propres. Pour les select box et les radio lists, il vérifie que les éléments sélectionnés provenaient bien des options proposées et qu'il n'y a pas eu de falsification. Nous avons déjà mentionné que pour les entrées de texte sur une seule ligne, il supprime les caractères de fin de ligne qu'un attaquant aurait pu y envoyer. Pour les entrées multilignes, il normalise les caractères de fin de ligne. Et ainsi de suite.
Nette résout pour vous les risques de sécurité dont beaucoup de programmeurs ne soupçonnent même pas l'existence.
L'attaque CSRF mentionnée consiste en ce qu'un attaquant attire la victime sur une page qui exécute discrètement dans le navigateur de la victime une requête vers le serveur sur lequel la victime est connectée, et le serveur croit que la requête a été exécutée par la victime de sa propre volonté. C'est pourquoi Nette empêche la soumission d'un formulaire POST depuis un autre domaine. Si pour une raison quelconque vous souhaitez désactiver la protection et autoriser la soumission du formulaire depuis un autre domaine, utilisez :
$form->allowCrossOrigin(); // ATTENTION ! Désactive la protection !
Cette protection utilise un cookie SameSite nommé _nss
. Créez donc l'objet formulaire avant d'envoyer la
première sortie, afin que le cookie puisse être envoyé.
La protection par cookie SameSite peut ne pas être fiable à 100%, il est donc conseillé d'activer également la protection par jeton :
$form->addProtection();
Nous recommandons de protéger ainsi les formulaires dans la partie administration du site, qui modifient des données
sensibles dans l'application. Le framework se défend contre l'attaque CSRF en générant et en vérifiant un jeton d'autorisation
qui est stocké dans la session. Il est donc nécessaire d'avoir une session ouverte avant d'afficher le formulaire. Dans la
partie administration du site, la session est généralement déjà démarrée en raison de la connexion de l'utilisateur. Sinon,
démarrez la session avec la méthode Nette\Http\Session::start()
.
Voilà, nous avons fait une rapide introduction aux formulaires dans Nette. Essayez de jeter un œil au répertoire examples dans la distribution, où vous trouverez plus d'inspiration.