1 / 20

🚀Les Fixtures dans Symfony 6.4

Formation DWWM - Développeur Web et Web Mobile

Maßtrisez l'art de créer des données de test professionnelles

Objectifs : Comprendre, installer et utiliser les fixtures Symfony

Durée : 2 heures

Niveau : Débutant à intermédiaire

📚Qu'est-ce qu'une Fixture ?

Définition simple

Une fixture = ensemble de données factices (mais réalistes) pour alimenter votre base de données

❌Sans fixtures

  • CrĂ©er manuellement des produits
  • Ajouter des catĂ©gories une par une
  • CrĂ©er des comptes utilisateurs
  • Passer des commandes fictives
  • = Perte de temps Ă©norme !

✅Avec fixtures

  • Automatisation complĂšte
  • DonnĂ©es en quelques secondes
  • CohĂ©rence entre dĂ©veloppeurs
  • Tests facilitĂ©s
  • = Gain de productivitĂ© !

🎯Cas d'usage concrets

Quand utiliser les fixtures ?

  • DĂ©veloppement : Tester vos fonctionnalitĂ©s avec des donnĂ©es cohĂ©rentes
  • Tests automatisĂ©s : Environnement de test reproductible
  • DĂ©monstrations : PrĂ©senter avec des donnĂ©es rĂ©alistes
  • Onboarding : Nouveaux dĂ©veloppeurs dĂ©marrent rapidement

Exemples par domaine

🛒 E-commerce : Produits, catĂ©gories, commandes, clients

📝 Blog : Articles, auteurs, commentaires, catĂ©gories

đŸ“± RĂ©seau social : Utilisateurs, posts, likes, followers

đŸ’Œ Gestion : EmployĂ©s, dĂ©partements, projets, tĂąches

⚙Installation et Configuration

1. Installation du bundle

# Installation via Composer
composer require --dev doctrine/doctrine-fixtures-bundle

# Le bundle est automatiquement activé

Note : --dev = uniquement en développement, pas en production

2. Vérification de l'installation

// config/bundles.php
return [
    // ... autres bundles
    Doctrine\Bundle\FixturesBundle\DoctrineFixturesBundle::class => [
        'dev' => true, 
        'test' => true
    ],
];

đŸ› ïžStructure de base d'une fixture

// src/DataFixtures/AppFixtures.php

namespace App\DataFixtures;

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

class AppFixtures extends Fixture
{
    public function load(ObjectManager $manager): void
    {
        // Création d'un utilisateur
        $user = new User();
        $user->setEmail('admin@example.com');
        $user->setName('Administrateur');
        
        // Persistance (prépare la sauvegarde)
        $manager->persist($user);
        
        // Sauvegarde définitive
        $manager->flush();
    }
}

🔍Comprendre le code

Les éléments clés

  • Namespace : App\DataFixtures
  • HĂ©ritage : extends Fixture
  • MĂ©thode : load() obligatoire
  • Manager : GĂšre la BDD

Le processus

  • persist() : PrĂ©pare l'objet
  • flush() : Sauvegarde rĂ©elle
  • new Entity() : CrĂ©e l'instance
  • setters : DĂ©finit les propriĂ©tĂ©s

Chargement des fixtures

# Chargement (supprime les données existantes)
php bin/console doctrine:fixtures:load

# Ajout sans supprimer
php bin/console doctrine:fixtures:load --append

đŸ‘šâ€đŸ’»Exemple pratique : Blog

public function load(ObjectManager $manager): void
{
    // Création d'un admin
    $admin = new User();
    $admin->setEmail('admin@blog.com');
    $admin->setName('Administrateur');
    $admin->setRoles(['ROLE_ADMIN']);
    $manager->persist($admin);
    
    // Création d'articles en boucle
    for ($i = 1; $i <= 10; $i++) {
        $article = new Article();
        $article->setTitle('Article ' . $i);
        $article->setContent('Contenu de l\'article ' . $i);
        $article->setAuthor($admin);
        $article->setCreatedAt(new \DateTimeImmutable());
        
        $manager->persist($article);
    }
    
    $manager->flush(); // Sauvegarde tout
}

Résultat : 1 admin + 10 articles en quelques secondes !

🔗Les rĂ©fĂ©rences entre fixtures

Pourquoi les références ?

Pour réutiliser des objets créés dans une fixture depuis une autre fixture

Créer une référence

// UserFixtures.php
public const ADMIN_REFERENCE = 'admin-user';

$admin = new User();
// ... configuration
$manager->persist($admin);

// Création de la référence
$this->addReference(
    self::ADMIN_REFERENCE, 
    $admin
);

Utiliser la référence

// ArticleFixtures.php
public function load(ObjectManager $manager): void
{
    // Récupération de la référence
    $admin = $this->getReference(
        UserFixtures::ADMIN_REFERENCE
    );
    
    $article = new Article();
    $article->setAuthor($admin);
    // ...
}

⚡GĂ©rer les dĂ©pendances

use Doctrine\Common\DataFixtures\DependentFixtureInterface;

class ArticleFixtures extends Fixture implements DependentFixtureInterface
{
    public function load(ObjectManager $manager): void
    {
        // Utilise des références de UserFixtures
        $admin = $this->getReference(UserFixtures::ADMIN_REFERENCE);
        // ...
    }
    
    // Déclare que cette fixture dépend de UserFixtures
    public function getDependencies(): array
    {
        return [UserFixtures::class];
    }
}

Important : Symfony charge automatiquement UserFixtures AVANT ArticleFixtures

🎭Des donnĂ©es rĂ©alistes avec Faker

Sans Faker

  • Nom : "Test 1", "Test 2"...
  • Email : "test@test.com"
  • Texte : "Contenu test"
  • = Peu professionnel

Avec Faker

  • Nom : "Marie Dubois", "Jean Martin"
  • Email : "marie.dubois@gmail.com"
  • Texte : Paragraphes rĂ©alistes
  • = DonnĂ©es professionnelles

Installation

composer require --dev fakerphp/faker

đŸŽČFaker en pratique

use Faker\Factory;

public function load(ObjectManager $manager): void
{
    $faker = Factory::create('fr_FR'); // En français
    
    for ($i = 0; $i < 20; $i++) {
        $user = new User();
        $user->setName($faker->name());                    // "Marie Dubois"
        $user->setEmail($faker->email());                  // "marie.d@gmail.com"
        $user->setPhone($faker->phoneNumber());            // "01 23 45 67 89"
        
        $manager->persist($user);
    }
    
    $manager->flush();
}

Méthodes utiles

  • $faker->word() - Un mot
  • $faker->sentence() - Une phrase
  • $faker->paragraph() - Un paragraphe
  • $faker->text(100) - Texte de 100 car.

Plus d'exemples

  • $faker->numberBetween(1, 100)
  • $faker->randomFloat(2, 0, 999)
  • $faker->dateTimeBetween('-1 year')
  • $faker->randomElement(['A', 'B'])

✹Exemple complet : E-commerce

public function load(ObjectManager $manager): void
{
    $faker = Factory::create('fr_FR');
    
    // Création de catégories
    $categories = [];
    for ($i = 0; $i < 5; $i++) {
        $category = new Category();
        $category->setName($faker->word());
        $category->setDescription($faker->sentence());
        $manager->persist($category);
        $categories[] = $category;
    }
    
    // Création de produits
    for ($i = 0; $i < 50; $i++) {
        $product = new Product();
        $product->setName($faker->productName());
        $product->setDescription($faker->text(200));
        $product->setPrice($faker->randomFloat(2, 10, 500));
        $product->setStock($faker->numberBetween(0, 100));
        $product->setCategory($faker->randomElement($categories));
        
        $manager->persist($product);
    }
    
    $manager->flush();
}

⚠Erreurs frĂ©quentes et solutions

❌ Erreur : "Class not found"

Cause : Bundle pas installé

Solution :

composer require --dev doctrine/doctrine-fixtures-bundle

❌ Erreur : "Reference not found"

Cause : Nom de référence incorrect

Solution : Utiliser des constantes

❌ Erreur : "Foreign key constraint"

Cause : Mauvais ordre de chargement

Solution : Implémenter DependentFixtureInterface

❌ Erreur : "MĂ©moire insuffisante"

Cause : Trop d'objets en mémoire

Solution : Flush par batch de 100

🚀Optimisation mĂ©moire

❌ ProblĂ©matique

// MAUVAIS - Garde tout en mémoire
for ($i = 0; $i < 10000; $i++) {
    $entity = new Entity();
    $manager->persist($entity);
}
$manager->flush(); // Boom ! đŸ’„

✅ Solution

// BON - Flush par batch
for ($i = 0; $i < 10000; $i++) {
    $entity = new Entity();
    $manager->persist($entity);
    
    // Tous les 100 objets
    if ($i % 100 === 0) {
        $manager->flush();
        $manager->clear(); // LibÚre la mémoire
    }
}
$manager->flush(); // Sauvegarde finale

📁Organisation des fixtures

Structure recommandée

src/DataFixtures/
├── UserFixtures.php          # Utilisateurs (en premier)
├── CategoryFixtures.php      # CatĂ©gories
├── ProductFixtures.php       # Produits (dĂ©pend des catĂ©gories)
├── OrderFixtures.php         # Commandes (dĂ©pend des users et produits)
└── AppFixtures.php           # Fixture globale (optionnelle)

✅ Bonnes pratiques

  • Une fixture par domaine
  • Respecter les dĂ©pendances
  • Utiliser des constantes
  • Documenter le code

❌ À Ă©viter

  • Tout dans une seule fixture
  • MĂ©langer dev/production
  • Oublier les dĂ©pendances
  • CrĂ©er trop de donnĂ©es d'un coup

🔧Commandes utiles

Commandes de base

# Charger les fixtures
php bin/console doctrine:fixtures:load

# Ajouter sans supprimer
php bin/console doctrine:fixtures:load --append

# Mode verbose (détaillé)
php bin/console doctrine:fixtures:load -v

# Sans confirmation
php bin/console doctrine:fixtures:load --no-interaction

Commandes de débogage

# Valider le schéma
php bin/console doctrine:schema:validate

# Voir les entités
php bin/console doctrine:mapping:info

# Exécuter du SQL
php bin/console doctrine:query:sql "SELECT COUNT(*) FROM user"

🎯Exercice pratique

Mission : Créer un blog complet

Entités : User, Article, Comment

Étape 1 : UserFixtures

  • 1 administrateur
  • 2 utilisateurs normaux
  • Utiliser Faker pour les donnĂ©es
  • CrĂ©er des rĂ©fĂ©rences

Étape 2 : ArticleFixtures

  • 6 articles par l'admin
  • 2 articles par utilisateur
  • Contenu Faker rĂ©aliste
  • DĂ©pendance vers UserFixtures

Étape 3 : CommentFixtures

🎓Pour aller plus loin

Fixtures avec services

class UserFixtures extends Fixture
{
    private UserPasswordHasherInterface $hasher;
    
    public function __construct(
        UserPasswordHasherInterface $hasher
    ) {
        $this->hasher = $hasher;
    }
    
    public function load(ObjectManager $manager): void
    {
        $user = new User();
        $password = $this->hasher->hashPassword(
            $user, 
            'password123'
        );
        $user->setPassword($password);
        $manager->persist($user);
        $manager->flush();
    }
}

Fixtures conditionnelles

public function load(ObjectManager $manager): void
{
    // Données selon l'environnement
    if ($_ENV['APP_ENV'] === 'dev') {
        // Beaucoup de données pour le dev
        $this->createManyEntities($manager, 1000);
    } else {
        // Peu de données pour les tests
        $this->createManyEntities($manager, 10);
    }
}

Fixtures avec images

use Symfony\Component\HttpFoundation\File\UploadedFile;

$imagePath = '/path/to/image.jpg';
$uploadedFile = new UploadedFile($imagePath, 'image.jpg', 'image/jpeg', null, true);
$product->setImage($uploadedFile);

📋Checklist de validation

✅ Avant de commencer

  • Bundle DoctrineFixturesBundle installĂ©
  • EntitĂ©s créées et configurĂ©es
  • Base de donnĂ©es créée
  • Migrations appliquĂ©es

✅ Pendant le dĂ©veloppement

  • Au moins 3 classes de fixtures
  • Utilisation des rĂ©fĂ©rences
  • DĂ©pendances implĂ©mentĂ©es
  • Faker intĂ©grĂ©

✅ Tests et validation

  • Test du chargement
  • VĂ©rification des donnĂ©es en BDD
  • Relations entre entitĂ©s validĂ©es
  • Test avec --append

✅ Bonnes pratiques

  • Constantes pour les rĂ©fĂ©rences
  • Flush par batch si nĂ©cessaire
  • Documentation du code
  • DonnĂ©es rĂ©alistes avec Faker

🚀RĂ©capitulatif et prochaines Ă©tapes

Ce que vous maĂźtrisez maintenant

  • Installation et configuration du bundle
  • CrĂ©ation de fixtures simples et complexes
  • Gestion des rĂ©fĂ©rences et dĂ©pendances
  • Utilisation de Faker pour des donnĂ©es rĂ©alistes
  • RĂ©solution des erreurs courantes

🎯 Prochaines Ă©tapes

  • Tests unitaires avec fixtures
  • Alice Bundle (fixtures en YAML)
  • Optimisation pour gros volumes
  • CI/CD avec fixtures

📚 Ressources

  • Documentation Symfony Fixtures
  • Documentation Faker PHP
  • Bonnes pratiques Symfony
  • CommunautĂ© Symfony France

đŸ’Ș Vous ĂȘtes maintenant prĂȘts Ă  crĂ©er des fixtures professionnelles !

Questions ? N'hésitez pas !