Symfony Fixtures Tutoriel : Comment Mettre en Place vos Données ?
Blog, Développement, Tutoriels

Symfony Fixtures Tutoriel : Comment Mettre en Place vos Données ?

Vous développez une application Symfony et votre base de données est vide ? Vous en avez marre de devoir créer manuellement un utilisateur ou des produits à chaque fois que vous réinitialisez votre base ? Ça vous fait perdre un temps fou.

La solution, ce sont les Data Fixtures. Elles permettent de remplir votre base avec un jeu de données de test propre et prévisible. Ce tutoriel pas à pas vous montre exactement comment les installer et les utiliser, même si vous partez de zéro.

Étape 1 : Installation et Configuration du Doctrine Fixtures Bundle

Pour commencer, vous devez installer le bundle qui gère les fixtures dans Symfony. Tout se passe avec une seule ligne de commande Composer. Ce tutoriel est à jour pour Symfony 6 et 7.

composer require --dev orm-fixtures

Vous avez vu l’option --dev ? C’est important. Elle indique que ce paquet est uniquement pour le développement. C’est simple : les fixtures ne vont jamais en production. Elles servent à préparer votre environnement de travail, pas à ajouter des données sur le site en ligne.

Après l’installation, Symfony met normalement à jour le fichier config/bundles.php tout seul. Jetez quand même un œil pour vérifier que cette ligne y est bien présente pour les environnements ‘dev’ et ‘test’ :

Doctrine\Bundle\FixturesBundle\DoctrineFixturesBundle::class => ['dev' => true, 'test' => true],

Pour plus de détails, vous pouvez consulter la documentation officielle du DoctrineFixturesBundle.

Étape 2 : Créer Votre Première Fixture (Exemple Simple)

Une fois le bundle installé, vous pouvez créer votre première fixture. Les fichiers de fixtures se trouvent dans le dossier `src/DataFixtures/`. Si ce dossier n’existe pas, créez-le.

À l’intérieur, créez un nouveau fichier PHP, par exemple `AppFixtures.php`. Chaque classe de fixture doit hériter de la classe `Fixture` fournie par le bundle. Le cœur de la fixture est la méthode `public function load(ObjectManager $manager)`.

Exemple de AppFixtures.php

Voici un exemple complet pour créer 10 produits. Vous pouvez adapter ce code avec votre propre `Entity`.

<?php

namespace App\DataFixtures;

use App\Entity\Product;
use Doctrine\Bundle\FixturesBundle\Fixture;
use Doctrine\Persistence\ObjectManager;

class AppFixtures extends Fixture
{
    public function load(ObjectManager $manager): void
    {
        for ($i = 0; $i < 10; $i++) {
            $product = new Product();
            $product->setName('Produit ' . $i);
            $product->setPrice(mt_rand(10, 100));
            $manager->persist($product);
        }

        $manager->flush();
    }
}

Analysons ce code très simple :

  • `namespace App\DataFixtures` : Indique que notre classe se trouve bien dans le bon dossier.
  • `class AppFixtures extends Fixture` : Notre classe hérite de la classe `Fixture` de base.
  • `public function load(ObjectManager $manager)` : C’est la méthode que le bundle va appeler pour exécuter la fixture. C’est ici que toute la logique se passe.
  • `$product = new Product()` : On crée une nouvelle instance de notre entité `Product`.
  • `$manager->persist($product)` : On dit à Doctrine de « surveiller » cet objet. Il n’est pas encore en base de données, mais il est prêt à être sauvegardé.
  • `$manager->flush()` : C’est l’étape la plus importante. Elle prend tous les objets que vous avez « persistés » et les envoie réellement dans la base de données. Sans `flush`, rien ne se passe.

Étape 3 : Exécuter les Fixtures

Maintenant que votre fixture est écrite, il faut dire à Symfony de la charger. Pour ça, on utilise une autre commande `console`.

Attention : Par défaut, la commande qui suit va purger (vider) toutes les tables de votre base de données avant de charger les nouvelles données. C’est fait exprès pour garantir un état propre à chaque fois.

Voici les commandes à connaître :

  • `symfony console doctrine:fixtures:load` : La commande principale. Elle vous demandera une confirmation, puis elle videra la base et chargera toutes vos classes de fixtures.
  • `symfony console doctrine:fixtures:load –append` : Utilisez cette option si vous voulez ajouter des données sans vider la base au préalable. C’est utile si vous ajoutez une nouvelle fixture et que vous ne voulez pas perdre les données existantes.
  • `symfony console doctrine:fixtures:load –purge-with-truncate` : Une méthode de purge plus « violente » qui réinitialise aussi les auto-incréments des ID. Pratique pour être sûr de repartir de zéro.

Aller plus loin : Fixtures avec Relations et Dépendances

C’est bien de créer des produits, mais que se passe-t-il si un `Product` doit être lié à une `Category` ? Vous devez gérer les relations entre vos entités.

Le problème est simple : vous devez créer la catégorie AVANT de créer le produit qui l’utilise. Pour ça, on utilise deux mécanismes : les références et les dépendances.

Utiliser les références pour lier des entités

Les références permettent de « sauvegarder » une entité dans une fixture pour la réutiliser dans une autre. On utilise `$this->addReference()` pour la sauvegarder et `$this->getReference()` pour la récupérer.

D’abord, créons une fixture pour les catégories, `CategoryFixtures.php` :

<?php

namespace App\DataFixtures;

use App\Entity\Category;
use Doctrine\Bundle\FixturesBundle\Fixture;
use Doctrine\Persistence\ObjectManager;

class CategoryFixtures extends Fixture
{
    public function load(ObjectManager $manager): void
    {
        $category = new Category();
        $category->setName('Électronique');
        $manager->persist($category);

        // On sauvegarde la catégorie avec une référence pour plus tard
        $this->addReference('category-electronique', $category);

        $manager->flush();
    }
}

Maintenant, modifions notre `ProductFixtures.php` pour utiliser cette référence :

<?php

// ... (use statements)
use App\DataFixtures\CategoryFixtures;

class ProductFixtures extends Fixture
{
    public function load(ObjectManager $manager): void
    {
        $product = new Product();
        $product->setName('Smartphone XYZ');
        // On récupère la catégorie créée dans CategoryFixtures
        $product->setCategory($this->getReference('category-electronique'));
        $manager->persist($product);

        $manager->flush();
    }
}

Gérer l’ordre de chargement avec DependentFixtureInterface

Le code ci-dessus a un problème : rien ne garantit que `CategoryFixtures` sera exécuté avant `ProductFixtures`. Pour forcer un ordre, `ProductFixtures` doit implémenter `DependentFixtureInterface`.

Voici la version finale de `ProductFixtures.php` :

<?php

namespace App\DataFixtures;

use App\Entity\Product;
use Doctrine\Bundle\FixturesBundle\Fixture;
use Doctrine\Persistence\ObjectManager;
use Doctrine\Common\DataFixtures\DependentFixtureInterface;

class ProductFixtures extends Fixture implements DependentFixtureInterface
{
    public function load(ObjectManager $manager): void
    {
        $product = new Product();
        $product->setName('Smartphone XYZ');
        $product->setPrice(599);
        $product->setCategory($this->getReference('category-electronique'));
        $manager->persist($product);
        
        $manager->flush();
    }

    public function getDependencies()
    {
        return [
            CategoryFixtures::class,
        ];
    }
}

Avec `getDependencies()`, vous dites à Symfony : « Attention, avant de lancer cette fixture, tu dois d’abord lancer `CategoryFixtures`« . Ça résout le problème de l’ordre de création.

FAQ – Questions fréquentes sur les Fixtures Symfony

Comment utiliser Faker pour générer des données aléatoires ?

Écrire des données à la main, c’est bien, mais pour des tests plus réalistes, vous pouvez utiliser une librairie comme Faker. Elle génère des noms, adresses, textes et plein d’autres choses de manière aléatoire.

  1. Installez Faker : `composer require –dev fakerphp/faker`
  2. Utilisez-le dans votre fixture :
use Faker\Factory;

public function load(ObjectManager $manager): void
{
    $faker = Factory::create('fr_FR');

    for ($i = 0; $i < 20; $i++) {
        $user = new User();
        $user->setEmail($faker->email);
        $user->setFirstName($faker->firstName);
        $manager->persist($user);
    }

    $manager->flush();
}

Peut-on utiliser les fixtures en production ?

NON, JAMAIS. Les fixtures sont conçues pour l’environnement de développement et de test. Elles écrasent les données et ne sont pas sécurisées pour un environnement de production. Le bundle est d’ailleurs installé en `–dev` pour cette raison.

Comment charger une seule fixture ou un groupe de fixtures ?

Vous pouvez organiser vos fixtures en groupes. Il suffit de faire implémenter `FixtureGroupInterface` à votre classe de fixture.

use Doctrine\Bundle\FixturesBundle\FixtureGroupInterface;

class MyFixture extends Fixture implements FixtureGroupInterface
{
    public static function getGroups(): array
    {
        return ['group1'];
    }
    // ... load method
}

Ensuite, vous pouvez lancer la commande avec l’option `–group` : `symfony console doctrine:fixtures:load –group=group1`.

La commande doctrine:fixtures:load ne fonctionne pas, que faire ?

Si la commande échoue, vérifiez ces points :

  • Connexion à la base de données : Votre fichier `.env.local` est-il bien configuré ? La base de données est-elle accessible ?
  • Bundle activé : Vérifiez que la ligne `DoctrineFixturesBundle` est bien dans `config/bundles.php`.
  • Namespace et nom de classe : Assurez-vous que le `namespace` de votre fichier de fixture correspond à son emplacement dans le dossier `src/DataFixtures/`.
  • Erreurs de code : Une erreur dans votre entité ou dans la fixture elle-même peut tout bloquer. Lisez attentivement le message d’erreur dans la console.

Vous pourriez également aimer...