Extension d'une contrainte Symfony pour ajouter vos propres règles de validation
🇬🇧
Dans cet article nous allons voir comment étendre une contrainte native Symfony dans le but d'ajouter une règle de validation personnalisée. Nous allons prendre un exemple concret avec la contrainte Email à laquelle nous allons ajouter une règle empêchant l'utilisateur de saisir une adresse email comportant un domaine sans extension.
Publié le 14/02/2019 par COil (Mis à jour le 21/02/2019)
Symfony Symfony4 validation email formPublié dans "Une semaine Symfonique n°633" sur Symfony.com
But
Le but ici va être de rendre les adresses du type "myemail@yahoo" invalides puisque l'extension du domaine est manquante. Ces adresses sont valides du point de vue de la RFC mais concrètement aucun des services utilisant un email ne fonctionnera avec. Veuillez noter que l'utilisation du mode "loose" de la contrainte native aboutira au même résultat, mais dans ce cas vous perdrez le bénéfice d'utiliser le mode strict. (par exemple l'email "toto@toto@toto.toto" sera valide avec le mode "loose")
Configuration
Cet article a été écrit en utilisant les composants suivants :
Pré-requis :
Nous allons supposer que vous êtes déjà familier avec Symfony, le composant Formulaire et que vous vous savez construire un formulaire basique avec un champ et des contraintes de validation. Si ce n'est pas le cas, allez vite lire la documentation ! 🤓
C'est parti !
Pour créer une nouvelle contrainte de formulaire, on a besoin de deux classes : La contrainte et le validateur.
La contrainte
Ce fichier est utilisé pour déclarer la configuration. Regardons le code : (ce snippet est le fichier utilisé dans ce projet affiché grâce à un helper Twig "voir le code source")
<?php
declare(strict_types=1);
namespace App\Component\Validator;
use Symfony\Component\Validator\Constraints\Email as BaseEmail;
/**
* New email validation rule, "forbid values without domain extension".
*/
class Email extends BaseEmail
{
final public const INVALID_DOMAIN_EXTENSION = '7da53a8b-56f3-4288-bb3e-ee9ede4ef9a2';
public bool $forbidEmptyDomainExtension = false;
public string $messageInvalidDomainExtension = 'bad_email_domain_extension';
}
Comme vous pouvez le voir, nous étendons la contrainte native Email
et nous avons trois éléments de configuration.
- La constante
INVALID_DOMAIN_EXTENSION
: C'est un code arbitraire pour identifier de manière unique la nouvelle règle que nous allons ajouter. $forbidEmptyDomainExtension
: Un attribut booléen qui va contrôler l'activation de notre nouvelle règle, elle sera désactivée par défaut pour garder le comportement original de la contrainte native.$messageInvalidDomainExtension
: Une clé utilisée pour la traduction du message d'erreur sur le formulaire.
Le validateur
<?php
declare(strict_types=1);
namespace App\Component\Validator;
use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\Constraints\EmailValidator as BaseEmailValidator;
/**
* Provide an additional check to avoid empty domain extensions.
*/
class EmailValidator extends BaseEmailValidator
{
public function validate($value, Constraint $constraint): void
{
parent::validate($value, $constraint);
if ($value === null || $value === '') {
return;
}
if (!$constraint instanceof Email) {
throw new \RuntimeException('Unexpected constraint type bound to the validator.');
}
if (!$constraint->forbidEmptyDomainExtension) {
return;
}
$domain = explode('@', (string) $value)[1] ?? '';
$extension = explode('.', $domain)[1] ?? false;
if (!$extension) {
$this->context->buildViolation($constraint->messageInvalidDomainExtension)
->setCode(Email::INVALID_DOMAIN_EXTENSION)
->addViolation();
}
}
}
Ici aussi, nous étendons le validateur natif. Dans la fonction principale validate()
nous appelons la méthode parent et nous continuons uniquement si nous avons une valeur à traiter. Ensuite, nous vérifions le type de la contrainte pour avoir l'autocomplétion sur les constantes et attributs de la classe Email
. Ensuite, nous vérifions l'activation de la nouvelle règle de validation. Le code principal du validateur va lui, effectivement vérifier si le domaine a bien une extension associée. Et enfin, si l'extension n'est pas trouvée, nous ajoutons une violation au contexte associé au validateur. Celle-ci sera utilisée pour afficher les erreurs sur le formulaire.
Utilisation
Pour utiliser notre nouvelle contrainte, remplacer l'instruction use
de votre formulaire comme ci-dessous :
<?php
declare(strict_types=1);
namespace App\Form\Type\User;
Ensuite, dans la fonction buildForm
de votre form type, utilisez la nouvelle contrainte et activez sa nouvelle règle de validation. (évidemment toutes les options standards de la contrainte native sont toujours disponibles)
public function buildForm(FormBuilderInterface $b, array $options): void
{
$b->add('email', EmailType::class, [
'label' => 'form.email',
'constraints' => [
new NotBlank(),
new Email(['mode' => 'strict', 'forbidEmptyDomainExtension' => true]),
]
]);
Et finallement, n'oubliez pas d'ajouter la traduction du message d'erreur dans votre fichier i18n :
# translations/validators.fr.yml
bad_email_domain_extension: >
Vous avez oublié l'extension du domaine de votre email, ex : ".com"
Et voilà ! 😁
Conclusion
C'était un exemple assez simple mais vous êtes bien sûr libres d'ajouter des règles plus complexes. Vous pouvez tester cette contrainte sur le formulaire d'inscription de ce site.
PS: Veuillez noter que le chemin des fichiers sera différent (le suffixe Bundle
sera à supprimer) si vous utilisez Flex : Tokeeen ne l'utilisant pas encore même s'il est propulsé par Symfony 4.2.
A vous de jouer !
Ces articles vous ont été utiles ? Vous pouvez nous aider à votre tour de plusieurs manières :
- Créez un compte et testez Tokeeen qui est encore en phase BETA.
- Vous inscrire à la newsletter et être averti du lancement officiel de notre service.
- Abonnez-vous au flux RSS.
- Suivez nous sur Twitter
- Nous remonter des erreurs ou typos (utilisez le formulaire de contact).
- Nous remonter des choses qui pourraient être améliorées.
Merci de nous avoir lu et à très bientôt sur Tokeeen ! 😉
Suivez C0il sur Twitter