Pourquoi utiliser les contraintes de validation plutôt que la validation de formulaire

Pourquoi utiliser les contraintes de validation plutôt que la validation de formulaire

Sam Rollin
Sam Rollin
January 10, 2026
Dernière mise à jour : February 15, 2026
January 10, 2026

Si vous avez construit des sites Drupal pendant un certain temps, vous avez probablement écrit pas mal de méthodes validateForm(). Elles fonctionnent, elles sont familières, et elles font le travail pour les vérifications de base. Mais à mesure que Drupal a mûri, surtout avec l'intégration plus étroite de Symfony dans Drupal 11, il existe un meilleur endroit pour la plupart de votre logique de validation : les contraintes de validation. Il ne s'agit pas de dire que la validation de formulaire est mauvaise ou dépréciée. Il s'agit de comprendre quel outil convient à quelle tâche, et pourquoi les contraintes sont devenues l'approche privilégiée pour tout ce qui touche à l'intégrité des données. Voyons ce que chaque approche fait, quand utiliser laquelle, et comment faire le bon choix pour vos projets.


Comprendre les deux voies de validation dans Drupal 11

Drupal 11 offre deux mécanismes distincts pour vérifier si les données sont valides. Ils servent des objectifs différents et opèrent à différentes couches du système.

Validation de formulaire : la couche interface utilisateur

La validation de formulaire se produit à l'intérieur du cycle de vie du formulaire. Quand un utilisateur soumet un formulaire, le FormBuilder de Drupal appelle validateForm() sur votre classe de formulaire, exécute tous les callbacks #element_validate que vous avez définis, et génère des erreurs qui empêchent la soumission si quelque chose ne va pas.

Ce processus est entièrement lié à ce formulaire spécifique. La logique de validation réside dans la classe du formulaire, s'exécute uniquement lors de la soumission du formulaire, et n'a aucun effet ailleurs dans votre système.

Voici à quoi ça ressemble typiquement :

public function validateForm(array &$form, FormStateInterface $form_state) {
  $email = $form_state->getValue('email');
  if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
    $form_state->setErrorByName('email', $this->t('Veuillez entrer une adresse courriel valide.'));
  }
}

Assez simple. Mais que se passe-t-il quand les mêmes données arrivent via une API REST? Ou sont importées via une migration? Cette validation ne s'exécute pas.

Contraintes de validation : la couche données

Les contraintes fonctionnent différemment. Elles sont attachées aux définitions de données (champs d'entité, propriétés de données typées, schémas de configuration) plutôt qu'aux formulaires. Quand vous appelez $entity->validate(), Drupal parcourt chaque champ et propriété, vérifiant chacun par rapport à ses contraintes définies.

Cette approche s'appuie sur le composant Validator de Symfony, que Drupal a intégré via son API Typed Data. Une contrainte se compose de deux parties : une classe de contrainte qui définit la règle et son message d'erreur, et une classe de validateur qui implémente la logique de vérification réelle.

// La définition de la contrainte
namespace Drupal\my_module\Plugin\Validation\Constraint;

use Symfony\Component\Validator\Constraint;

/**
 * @Constraint(
 *   id = "UniqueProductSku",
 *   label = @Translation("SKU de produit unique")
 * )
 */
class UniqueProductSkuConstraint extends Constraint {
  public $message = 'Le SKU %value est déjà utilisé.';
}

// Le validateur qui fait le travail
namespace Drupal\my_module\Plugin\Validation\Constraint;

use Symfony\Component\Validator\ConstraintValidator;

class UniqueProductSkuConstraintValidator extends ConstraintValidator {
  public function validate($value, Constraint $constraint) {
    // Vérifier si le SKU existe dans la base de données
    if ($this->skuExists($value)) {
      $this->context->addViolation($constraint->message, ['%value' => $value]);
    }
  }
}

Une fois cette contrainte attachée à un champ, elle s'exécute peu importe comment les données arrivent : formulaires, appels API, migrations, création programmatique. Une seule définition, une application cohérente.

Pourquoi les contraintes sont plus logiques pour les règles d'affaires

Le virage vers les contraintes n'est pas arbitraire. Il reflète comment Drupal est réellement utilisé en 2025.

Source unique de vérité

Quand vous mettez la logique de validation dans une contrainte, vous la définissez une seule fois. Cette règle « le SKU du produit doit être unique et correspondre à ce patron » existe à un seul endroit et s'applique partout. Vous ajoutez un nouveau formulaire qui modifie les produits? La contrainte s'exécute quand même. Vous construisez un script d'importation personnalisé? Les mêmes règles s'appliquent. Vous exposez l'entité via JSON:API? Toujours validée.

Avec la validation de formulaire, vous finissez par copier la logique entre les formulaires, ou pire, vous oubliez de l'ajouter aux nouveaux formulaires. On a tous hérité de projets où le formulaire d'administration validait quelque chose que le formulaire public ne validait pas, et personne ne s'en est rendu compte jusqu'à ce que des données invalides commencent à apparaître.

Architectures API et découplées

Beaucoup de projets Drupal 11 ne reposent pas principalement sur les formulaires de Drupal. Des interfaces React ou Vue soumettent des données via JSON:API. Des applications mobiles utilisent des points d'accès REST. Des systèmes externes poussent du contenu via des intégrations personnalisées.

Aucun de ces cas ne touche à la couche formulaire de Drupal. Si votre validation réside dans validateForm(), elle n'existe tout simplement pas pour ces cas d'utilisation. Les contraintes, par contre, peuvent être vérifiées partout où vous avez accès à l'entité ou à l'objet de données typées.

Ce n'est pas une préoccupation théorique. On a nettoyé pas mal de projets où les formulaires web fonctionnaient parfaitement mais l'API laissait passer des données invalides parce que personne n'avait pensé à dupliquer les règles de validation.

Architecture plus propre

Les formulaires ont déjà beaucoup de responsabilités : construire le tableau de rendu, gérer les flux multi-étapes, traiter les soumissions, gérer les redirections. Ajouter la validation des règles d'affaires par-dessus rend les classes de formulaire gonflées et plus difficiles à tester.

Les contraintes gardent « est-ce que ces données sont valides? » séparé de « comment ce formulaire fonctionne-t-il? ». Vous pouvez tester unitairement un validateur de contrainte isolément. Vous pouvez le réutiliser à travers les types d'entités. Vous pouvez le documenter comme la définition faisant autorité de ce à quoi ressemblent des données valides.

Alignement avec la direction de Drupal

Le cœur de Drupal s'est orienté régulièrement vers la validation basée sur les contraintes depuis Drupal 8. La documentation de l'API de validation des entités décrit explicitement les contraintes comme découplées de la validation de formulaire et encourage à mettre la logique personnalisée dans les classes de contraintes.

L'intégration de Drupal 11 avec Symfony 7.x signifie que vous utilisez un composant de validation actuel et activement maintenu. L'écosystème de contraintes Symfony (NotNull, Length, Range, Email, et bien d'autres) fonctionne nativement aux côtés des ajouts propres à Drupal.

Quand la validation de formulaire a encore du sens

Cela ne veut pas dire que la validation de formulaire est obsolète. Il existe des cas légitimes où valider au niveau du formulaire est le bon choix.

Vérifications spécifiques à l'interface

Certaines validations n'ont de sens que dans le contexte d'un formulaire particulier. Les vérifications « confirmez que votre mot de passe correspond » appartiennent au formulaire d'inscription, pas à l'entité utilisateur. Un assistant multi-étapes pourrait avoir besoin de s'assurer que certains champs sont remplis avant d'avancer, même si ces champs ne sont pas techniquement requis sur l'entité finale.

Exigences conditionnelles de champs

Les formulaires ont souvent une logique conditionnelle : afficher le champ B seulement si la case A est cochée, rendre le champ C obligatoire seulement quand le menu déroulant D a une valeur spécifique. Ces conditions peuvent être purement présentationnelles ou s'appliquer seulement à ce flux de travail particulier, pas au modèle de données sous-jacent.

Règles dépendantes de la session ou du contexte

Parfois, la validation dépend de l'état de session de l'utilisateur actuel, de l'étape où il se trouve dans un processus, ou d'informations qui n'existent que pendant l'interaction avec le formulaire. Ces vérifications ne peuvent pas facilement être exprimées comme des contraintes au niveau des données.

Notre expérience montre qu'environ 80 % de la logique de validation appartient au niveau des contraintes, avec seulement les préoccupations UX spécifiques aux formulaires qui restent dans validateForm(). La question clé est : « Cette règle devrait-elle s'appliquer si les données arrivaient par un autre canal? » Si oui, c'est une contrainte. Si c'est purement une question d'expérience utilisateur pour ce formulaire, gardez-la dans le formulaire.

Prendre la décision : un cadre pratique pour la validation Drupal

Quand vous ajoutez de la validation à un projet Drupal 11, passez en revue ces questions :

Cette règle s'applique-t-elle aux données elles-mêmes, ou juste à ce formulaire? Si un SKU de produit doit toujours être unique, c'est une règle de données, utilisez une contrainte. Si vous avez besoin que les utilisateurs confirment que leur adresse courriel correspond à ce qu'ils ont tapé, c'est une préoccupation de flux de formulaire.

Ces données pourraient-elles arriver par un canal autre qu'un formulaire? Points d'accès API, migrations, commandes Drush, création programmatique d'entités, si l'un de ces cas s'applique maintenant ou pourrait s'appliquer plus tard, mettez la validation dans une contrainte.

Cette règle concerne-t-elle l'intégrité des données ou l'expérience utilisateur? « Le champ ne doit pas être vide » concerne souvent l'intégrité des données. « Afficher un avertissement en ligne quand l'utilisateur n'a pas rempli ce champ optionnel mais recommandé » c'est de l'UX.

Devrais-je dupliquer cette logique dans un autre formulaire? Si vous êtes tenté de copier-coller, c'est un signe que la logique appartient à une contrainte.

On a constaté qu'appliquer ces questions de façon cohérente mène à des bases de code plus propres. Les contraintes gèrent la question « qu'est-ce que des données valides », les formulaires gèrent la question « comment les utilisateurs interagissent-ils avec ça », et il y a un chevauchement minimal entre les deux.

Implémenter les contraintes en pratique

Attacher une contrainte à un champ d'entité dans Drupal 11 suit un patron cohérent :

// Dans hook_entity_base_field_info_alter() ou similaire
function my_module_entity_base_field_info_alter(&$fields, $entity_type) {
  if ($entity_type->id() === 'node' && isset($fields['field_product_sku'])) {
    $fields['field_product_sku']->addConstraint('UniqueProductSku');
  }
}

Pour les définitions de champs en config, vous pouvez ajouter des contraintes via les paramètres du champ. Pour les types d'entités personnalisés, définissez les contraintes directement dans votre méthode baseFieldDefinitions().

L'essentiel est d'appeler $entity->validate() avant de sauvegarder quand vous travaillez programmatiquement :

$violations = $entity->validate();
if ($violations->count() > 0) {
  foreach ($violations as $violation) {
    // Gérer ou journaliser la violation
    $this->logger->error($violation->getMessage());
  }
  throw new ValidationException('L\'entité a échoué la validation.');
}
$entity->save();

Les formulaires qui utilisent des classes de formulaire d'entité s'intègrent déjà avec ce système, les violations des contraintes apparaîtront automatiquement comme erreurs de formulaire. Pour les formulaires personnalisés ou les gestionnaires d'API, vous devrez vérifier les violations explicitement et les faire remonter de façon appropriée.

Erreurs courantes à éviter

Dupliquer la logique aux deux endroits. Si vous avez une contrainte et vérifiez aussi la même règle dans validateForm(), vous allez confondre les futurs développeurs et risquer que les implémentations divergent. Choisissez un seul emplacement.

Oublier d'appeler validate() dans le code personnalisé. Les contraintes n'empêchent pas automatiquement $entity->save() de s'exécuter. Si vous créez des entités programmatiquement sans validation, des données invalides peuvent quand même passer. Intégrez la validation dans vos flux de sauvegarde.

Trop contraindre au niveau des données. Tout n'a pas besoin d'être une contrainte stricte. Certains champs peuvent être « recommandés » plutôt que « requis », et c'est une préoccupation UX mieux gérée par des indices et avertissements dans le formulaire que comme erreurs de validation.

Ignorer les contraintes intégrées. Avant d'écrire une contrainte personnalisée, vérifiez ce que Drupal et Symfony fournissent déjà. Length, Range, NotBlank, Email, Regex, et bien d'autres sont prêtes à utiliser.

En résumé

Le choix entre les contraintes de validation et la validation de formulaire n'est pas vraiment une compétition. Ils servent des objectifs différents. Les contraintes gèrent les règles d'intégrité des données qui doivent s'appliquer partout où vos entités sont créées ou modifiées. La validation de formulaire gère les préoccupations spécifiques à l'interface liées à des flux de travail utilisateur particuliers.

Pour les projets Drupal 11, surtout ceux avec exposition API ou plusieurs points d'entrée de données, traiter les contraintes comme le mécanisme de validation principal mène à des applications plus cohérentes, maintenables et fiables. La validation de formulaire reste utile pour ce pour quoi elle est conçue : gérer l'expérience de l'utilisateur dans un formulaire spécifique.

Les équipes avec qui on travaille rapportent que déplacer les règles d'affaires vers les contraintes réduit typiquement les bogues de validation et rend la base de code plus facile à comprendre. Ça demande un certain ajustement si vous êtes habitué à tout mettre dans validateForm(), mais le retour en qualité de données et en clarté architecturale en vaut la peine.

Si vous planifiez un projet Drupal 11 ou refactorisez la logique de validation d'un site existant, on peut vous aider à identifier quelles règles appartiennent au niveau des contraintes versus au niveau du formulaire et mettre en place une architecture de validation qui protège vos données sur tous les canaux. Contactez-nous pour discuter des besoins spécifiques de votre projet.

Share this article