Extension d'une contrainte Symfony pour ajouter vos propres règles de validation

🇬🇧 

Your browser uses a different language of the article's one. Do you want to read it in English?

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  form 

Publié dans "Une semaine Symfonique n°633" sur Symfony.com

Extension d'une contrainte Symfony pour ajouter vos propres règles de validation

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.

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 :


Merci de nous avoir lu et à très bientôt sur Tokeeen ! 😉

COil


profile for Tokeeen.com at Stack Overflow, Q&A for professional and enthusiast programmers