Désactiver la validation HTML5 de tous vos formulaires Symfony avec un interrupteur de fonctionnalité

Publié le 29/06/2019 • Actualisé le 01/07/2019

Dans cet article nous allons voir comment implémenter un interrupteur de fonctionnalité basique, en anglais "feature flag". Cet interrupteur va nous permettre d'activer ou de désactiver une fonctionnalité à la demande. Dans ce cas, il va nous permettre de débugger plus facilement nos formulaires en désactivant la validation HTML5 coté client pour tous les formulaires d'une application. C'est parti ! 😎


English language detected! 🇬🇧

  We noticed that your browser is using English. Do you want to read this post in this language?

Read the english version 🇬🇧 Close

» Publié dans "Une semaine Symfonique 653" (du 1 au 7 juillet 2019).

Configuration

  • PHP 8.3
  • Symfony 6.4.6

Introduction

Quand on travaille sur la validation coté serveur d'un formulaire, il est plus facile de débugger si la validation HTML5 coté client est désactivée. Pour ce faire vous pouvez ajouter à votre formulaire l'attribut novalidate dans votre template Twig :

{{ form_start(form, {attr: {'novalidate': 'novalidate'}}) }}

Une petite astuce ici : Vous pouvez déclarer un attribut invalide afin de remettre en place la validation. C'est plus facile que de le supprimer complètement. Mais ce n'est pas très propre puisque votre formulaire contiendra donc un attribut invalide et inutile qui ne sera pas interprété par le navigateur.

{{ form_start(form, {attr: {'novalidate-off': 'novalidate'}}) }}

Vous pouvez aussi déclarer cet attribut directement dans la classe FormType de votre formulaire, dans ce cas on peut commenter ou pas la ligne en question :

public function configureOptions(OptionsResolver $resolver): void
{
    $resolver->setDefaults([
        'attr' => [
            'novalidate' => 'novalidate', // comment me to reactivate the html5 validation!  🚥
        ]
    ]);
}

Ça fonctionne mais c'est une tâche manuelle et ce code de débug peut être oublié. Donc voyons comment nous pouvons automatiser cette tâche, pas seulement pour un formulaire mais pour tous les formulaires de notre application.

Création de l'interrupteur de fonctionnalité

Premièrement, voyons comment créer cet interrupteur. Implémentons-le de la manière la plus simple qui soit : nous allons déclarer un paramètre de type booléen dans le conteneur de services.

# config/posts/29.yaml (imported in config/services.yaml)
parameters:
    html5_validation: '%env(bool:APP_HTML5_VALIDATION)%'

Nous avons besoin d'assigner une variable à ce paramètre afin de pouvoir l'injecter dans un service :

# config/services.yaml
services:
    _defaults:
        bind:
            $html5Validation: '%html5_validation%'

Maintenant que nous pouvons utiliser ce paramètre dans un service, créons l'extension de type qui va être responsable de désactiver ou pas notre fonctionnalité.

Création de la nouvelle extension de type

Le but ici va être d'ajouter une extension globale de formulaire qui sera utilisée par l'ensemble de nos classes FormType. Créez le fichier suivant. Grâce à l'autoloading ce service sera automatiquement utilisé par l'application. De plus, celui-ci sera tagué en tant qu'extension de FormType puisque qu'il implémente l'interface AbstractTypeExtension.

<?php

declare(strict_types=1);

// src/Form/Extension/NoValidateExtension.php

namespace App\Form\Extension;

use Symfony\Component\Form\AbstractTypeExtension;
use Symfony\Component\Form\Extension\Core\Type\FormType;
use Symfony\Component\Form\FormInterface;
use Symfony\Component\Form\FormView;

class NoValidateExtension extends AbstractTypeExtension
{
    private bool $html5Validation;

    public function __construct(bool $html5Validation)
    {
        $this->html5Validation = $html5Validation;
    }

    /**
     * @param array<string,mixed> $options
     */
    public function buildView(FormView $view, FormInterface $form, array $options): void
    {
        $attr = !$this->html5Validation ? ['novalidate' => 'novalidate'] : [];
        $view->vars['attr'] = array_merge($view->vars['attr'], $attr);
    }

    public static function getExtendedTypes(): iterable
    {
        return [FormType::class];
    }
}

Comme vous pouvez le voir, nous sauvons l'état du paramètre et suivant sa valeur nous ajoutons l'attribut novalidate au formulaire à l'aide de la variable $view->vars['attr'] de la méthode buildView().
La valeur nominale de l'interrupteur sera donc à vrai. Quand nous voudrons désactiver, nous la passerons donc à faux dans notre fichier services.yaml. Mais voyez-vous le problème ? Si on commit par inadvertance le paramètre à faux, on pourrait se retrouver en production avec toute la validation côté client désactivée. Même si l'application fonctionnerait sans, grâce à la validation côté serveur que permet Symfony, ce n'est probablement pas quelque chose que vous voulez faire.
Afin de rendre ce paramètre lié à l'environnement utilisé nous allons le déporter dans le fichier .env se trouvant à la racine de l'application. Ainsi en production nous mettrons cette valeur à vrai (1) et nous n'y toucherons plus. Par contre, en développement nous pourrons le changer comme bon nous semble. Ajoutez le paramètre suivant dans le fichier .env :

# .env and .env.dist
APP_HTML5_VALIDATION=true

Dans le fichier services.yml, remplaçons la valeur en dur du paramètre par celle que nous venons d'introduire :

# config/services.yaml
parameters:
    html5_validation: '%env(bool:APP_HTML5_VALIDATION)%'

Vous remarquerez que nous avons utilisé le préprocesseur bool afin d'indiquer au composant .env que cette variable est de type booléen et non une chaîne de caractères.

Citation de @fabpot : "Une variable d'environnement est une chaîne par design (essence)".

En effet sans ce pré-traitement, si nous indiquons false comme valeur, celle-ci sera interprétée comme vrai car le type hint du constructeur de notre classe attend un booléen __construct(bool $htm5Validation). Testez pour voir ! On peut aussi mettre comme valeur 0 ou 1 directement sans utiliser le préprocesseur.

Maintenant nous sommes sûrs que cette valeur restera à vrai en production mais nous pourrons facilement la modifier pendant que nous développons.

Et voilà ! J'espère que vous avez aimé. Découvrez d'autres informations en rapport à cet article avec les liens ci-dessous. Comme toujours, retours, likes et retweets sont les bienvenus. (voir la boîte ci-dessous) À tantôt ! COil. 😊

 La documentation  Plus sur Stackoverflow  Plus sur le web

Ils m'ont donné leurs retours et m'ont aidé à corriger des erreurs et typos dans cet article, un grand merci à : ro0NL, jmsche.

  Travaillez avec moi !


A vous de jouer !

Ces articles vous ont été utiles ? Vous pouvez m'aider à votre tour de plusieurs manières : (cf le tweet à droite pour me contacter )

  • Me remonter des erreurs ou typos.
  • Me remonter des choses qui pourraient être améliorées.
  • Aimez et retweetez !
  • Suivez moi sur Twitter
  • Inscrivez-vous au flux RSS.
  • Cliquez sur les boutons Plus sur Stackoverflow pour me faire gagner des badges "annonceur" 🏅.

Merci d'avoir tenu jusque ici et à très bientôt sur Strangebuzz ! 😉

COil