Valider vos données factices avec le bundle Symfony Alice

Publié le 15/10/2022 • Actualisé le 15/10/2022

Dans cet article, nous voyons comment valider des données factices avec le bundle Symfony Alice avant de les insérer en base de données. C'est l'essentiel afin de ne pas avoir de données invalides en environnements de développement ou de test. 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

Prérequis

Je présumerai que vous avez au moins les connaissances de base de Symfony et que vous savez charger des données factices avec le bundle hautelook/alice-bundle.

Configuration

  • PHP 8.3
  • Symfony 6.4.12
  • hautelook/alice-bundle 2.11

La référence composer hautelook/alice-bundle, pointe en fait vers https://github.com/theofidry/AliceBundle, cliquez ici pour comprendre pourquoi.

Introduction

C'est quelque chose qui n'est a priori pas si évident. J'utilise le bundle Alice depuis longtemps, et c'est quelque chose dont je ne m'étais pas rendu compte. Je l'ai réalisé quand quelqu'un a posé une question à ce sujet sur le canal #alice-fixtures du slack Symfony dev. J'ai vérifié, et en effet, la personne avait raison ; par défaut les fixtures ne sont pas validées. 😮

But

Le but ici va être de créer un simple processeur qui va vérifier que chaque fixture est valide avant de l'insérer en base. Nous allons, bien sûr, utiliser le validateur Symfony.

Le problème

Analysons le problème, voici les fixtures correspondant à cet article :

  article_224 (extends article):
    previous_article: '@article_216'
    next_article: '@article_232'
    name: 'Validating your data fixtures with the Alice Symfony bundle'
    date_published: <date_create('2022-10-15')>
    date_modified: <date_create('2022-10-15')>
    keyword: 'symfony,fixtures,alice,validation,processor,data'
    time_required: 3
    stackoverflow_url: https://stackoverflow.com/q/28641360/633864
    icon: fad fa-box-check

Ces fixtures étendent le modèle article :

App\Entity\Article:
  article (template):
    #type: !php/const App\DBAL\Types\ArticleType::TYPE_BLOG_POST
    type: !php/const App\Enum\ArticleType::BLOG_POST
    active: true
    author: <{pseudo}>
    publisher: '@organization_strangebuzz'
    in_language: <{locales_list}>

Maintenant, ajoutons une contrainte, par exemple, sur la propriété Author de l'entité Article. Ce n'est désormais plus un texte libre, mais il doit correspondre à certaines valeurs. Pour ce faire, nous pouvons utiliser la contrainte Symfony Choice.

    private const array ALLOWED_AUTHORS = ['COil', 'Soyuka', 'Dunglas'];

    /**
     * The author of this content or rating. Please note that author is special in that HTML 5 provides a special mechanism for indicating authorship via the rel tag. That is equivalent to this and may be used interchangeably.
     */
    #[ORM\Column(type: 'text', nullable: false)]
    #[Assert\NotBlank]
    #[Assert\Choice(choices: self::ALLOWED_AUTHORS, message: '{{ value }} is an invalid value for the author! Allowed values are: {{ choices }}')]
    #[Groups(groups: [Article::GROUP_DEFAULT])]

Dans ces fixtures, mettons une valeur autre que celles autorisées :

  article_224 (extends article):
    author: foobar

Si nous rechargeons les données, comme prévu, on n'a pas d'erreur. Dans la base de données, nous avons la valeur foobar dans le champ auteur.

La solution ✨

Le bundle est extensible et permet d'effectuer des traitements avant et après insertion en base grâce aux processeurs ; ajoutons en un :

<?php

declare(strict_types=1);

namespace App\DataFixtures\Processor;

use App\Entity\Article;
use Fidry\AliceDataFixtures\ProcessorInterface;
use Symfony\Component\Validator\ConstraintViolationList;
use Symfony\Component\Validator\Validator\ValidatorInterface;

final class ArticleProcessor implements ProcessorInterface
{
    public function __construct(
        private readonly ValidatorInterface $validator,
    ) {
    }

    public function preProcess(string $id, object $object): void
    {
        if (!$object instanceof Article) {
            return;
        }

        /** @var ConstraintViolationList $violations */
        $violations = $this->validator->validate($object);
        if ($violations->count() > 0) {
            $message = \sprintf("Error when validating fixture %s (Article), violation(s) detected:\n%s", $id, $violations);
            throw new \DomainException($message);
        }
    }

    public function postProcess(string $id, object $object): void
    {
    }
}

Grâce à Symfony, ce service est auto-configuré. Sinon, on doit lui assigner l'étiquette (tag) fidry_alice_data_fixtures.processor.

Information for Service "App\DataFixtures\Processor\ArticleProcessor"
=====================================================================

---------------- ---------------------------------------------
Option           Value
---------------- ---------------------------------------------
Service ID       App\DataFixtures\Processor\ArticleProcessor
Class            App\DataFixtures\Processor\ArticleProcessor
Tags             fidry_alice_data_fixtures.processor
Public           no
Synthetic        no
Lazy             no
Shared           yes
Abstract         no
Autowired        yes
Autoconfigured   yes
---------------- ---------------------------------------------

Quelques explications. On modifie la fonction preProcess() car nous voulons valider les données avant l'insertion en base. Le premier argument string $id est l'identifiant de la fixture, pour cet article, c'est article_224. Le second argument est l'entité qui est en cours de traitement. Dans cet exemple, je ne déclenche la validation que si c'est une entité de type Article mais on peut le faire pour toutes les entités. Dans ce cas, retirez le test instanceof. Maintenant, essayons de recharger les données :

php bin/console doctrine:cache:clear-metadata
php bin/console doctrine:database:create --if-not-exists
php bin/console doctrine:schema:drop --force
php bin/console doctrine:schema:create
php bin/console doctrine:schema:validate
php bin/console hautelook:fixtures:load --no-interaction

In ArticleProcessor.php line 29:

Error when validating fixture article_224 (Article), violation(s) detected:
Object(App\Entity\Article).author:
"foobar" is an invalid value for the author! Allowed values are: "COil", "Soyuka", "Dunglas" (code 8e179f1b-97aa-4560-a02f-2a8b42e49df7)

hautelook:fixtures:load [-b|--bundle [BUNDLE]] [--no-bundles] [-m|--manager MANAGER] [--append] [--shard SHARD] [--purge-with-truncate]
make: *** [load-fixtures] Error 1

Victoire, on a désormais une erreur, et le chargement des données est interrompu (Rien n'est chargé). On a un message de débogage explicite avec l'identifiant de la fixture concernée et la liste des violations à corriger.

Conclusion

Nous avons vu comment valider des données factices Alice avec un processeur. Ça a été rapide à ajouter et dorénavant, nous sommes sûrs de ne plus insérer de données incorrectes dans la base de données. Ça a un léger coût en performances comme le validateur est appelé pour chaque fixture, mais c'est assez insignifiant au regard des tests additionnels ajoutés.

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) À bientôt ! COil. 😊

  Lire la doc  Plus sur Stackoverflow

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

  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