Charger des fixtures en Symfony (Doctrine Fixtures Bundle)
Les fixtures Doctrine sont indispensables pour alimenter votre base de donnees de developpement avec des donnees coherentes et reproductibles. Elles accelerent le developpement, facilitent les tests et permettent a toute l'equipe de travailler avec le meme jeu de donnees. Voici comment les maitriser avec Symfony 7.2.
Installation du DoctrineFixturesBundle
Le bundle s'installe en une commande. Il est recommande de l'ajouter uniquement en dependance de dev :
composer require --dev doctrine/doctrine-fixtures-bundle
Symfony Flex configure automatiquement le bundle. Vous pouvez verifier dans config/bundles.php que la ligne suivante est presente :
// config/bundles.php
return [
// ...
Doctrine\Bundle\FixturesBundle\DoctrineFixturesBundle::class => ['dev' => true, 'test' => true],
];
Le bundle est active uniquement en environnement dev et test - il ne sera pas charge en production, ce qui est le comportement souhaite. Pour une installation complete de Symfony, consultez notre guide d'installation 2026.
Creer votre premiere fixture
Les fixtures sont des classes PHP qui implementent FixtureInterface (ou plus couramment, qui etendent Fixture). Placez-les dans src/DataFixtures/ :
<?php
namespace App\DataFixtures;
use App\Entity\Category;
use Doctrine\Bundle\FixturesBundle\Fixture;
use Doctrine\Persistence\ObjectManager;
class CategoryFixtures extends Fixture
{
public const CATEGORIES = [
'electronique' => 'Electronique',
'vetements' => 'Vetements',
'livres' => 'Livres',
'maison' => 'Maison & Jardin',
'sport' => 'Sport & Loisirs',
];
public function load(ObjectManager $manager): void
{
foreach (self::CATEGORIES as $slug => $name) {
$category = new Category();
$category->setName($name);
$category->setSlug($slug);
$manager->persist($category);
// Stocker une reference pour les autres fixtures
$this->addReference('category_' . $slug, $category);
}
$manager->flush();
}
}
Chargez les fixtures avec la commande suivante :
# Charger toutes les fixtures (PURGE la base avant)
php bin/console doctrine:fixtures:load
# Charger sans confirmation
php bin/console doctrine:fixtures:load --no-interaction
# Ajouter sans purger (attention aux doublons)
php bin/console doctrine:fixtures:load --append
Attention : par defaut, la commande purge toute la base avant de charger les fixtures. C'est voulu - cela garantit un etat propre et reproductible. N'utilisez --append que si vous savez exactement ce que vous faites.
References entre fixtures
Les references permettent a une fixture de recuperer des entites creees par une autre fixture. C'est indispensable pour les relations :
<?php
namespace App\DataFixtures;
use App\Entity\Product;
use Doctrine\Bundle\FixturesBundle\Fixture;
use Doctrine\Common\DataFixtures\DependentFixtureInterface;
use Doctrine\Persistence\ObjectManager;
class ProductFixtures extends Fixture implements DependentFixtureInterface
{
public function load(ObjectManager $manager): void
{
$products = [
[
'name' => 'Smartphone Galaxy S26',
'price' => 899.99,
'category' => 'category_electronique',
],
[
'name' => 'T-shirt coton bio',
'price' => 29.90,
'category' => 'category_vetements',
],
[
'name' => 'Clean Code - Robert C. Martin',
'price' => 34.50,
'category' => 'category_livres',
],
];
foreach ($products as $data) {
$product = new Product();
$product->setName($data['name']);
$product->setPrice($data['price']);
$product->setCategory($this->getReference(
$data['category'],
\App\Entity\Category::class
));
$product->setActive(true);
$product->setCreatedAt(new \DateTimeImmutable());
$manager->persist($product);
}
$manager->flush();
}
public function getDependencies(): array
{
return [
CategoryFixtures::class,
];
}
}
La methode getDependencies() garantit que CategoryFixtures sera executee avant ProductFixtures. Sans cela, l'appel a getReference() echouerait car les categories n'existeraient pas encore.
Ordre de chargement : getDependencies vs getOrder
Il existe deux approches pour controler l'ordre d'execution des fixtures :
Approche 1 : DependentFixtureInterface (recommandee)
class ProductFixtures extends Fixture implements DependentFixtureInterface
{
public function getDependencies(): array
{
return [
CategoryFixtures::class,
BrandFixtures::class,
];
}
}
Cette approche est declarative et auto-documentee. Doctrine resout automatiquement l'arbre de dependances, meme avec des dependances transitives.
Approche 2 : OrderedFixtureInterface
class CategoryFixtures extends Fixture implements OrderedFixtureInterface
{
public function getOrder(): int
{
return 1; // Execute en premier
}
}
class ProductFixtures extends Fixture implements OrderedFixtureInterface
{
public function getOrder(): int
{
return 2; // Execute en second
}
}
L'approche numerique est plus fragile - ajoutez une fixture entre 1 et 2 et vous devrez re-numeroter. Privilegiez getDependencies().
Groupes de fixtures
Les groupes permettent de charger un sous-ensemble de fixtures. C'est pratique quand vous avez des fixtures volumineuses pour les tests de performance et des fixtures legeres pour le dev quotidien :
<?php
namespace App\DataFixtures;
use Doctrine\Bundle\FixturesBundle\Fixture;
use Doctrine\Bundle\FixturesBundle\FixtureGroupInterface;
use Doctrine\Persistence\ObjectManager;
class HeavyProductFixtures extends Fixture implements FixtureGroupInterface
{
public static function getGroups(): array
{
return ['heavy', 'performance'];
}
public function load(ObjectManager $manager): void
{
// Generer 10 000 produits pour les tests de performance
for ($i = 0; $i < 10000; $i++) {
$product = new Product();
$product->setName("Product benchmark #$i");
$product->setPrice(mt_rand(100, 99999) / 100);
$product->setActive(true);
$manager->persist($product);
// Flush par batch pour eviter la saturation memoire
if ($i % 500 === 0) {
$manager->flush();
$manager->clear();
}
}
$manager->flush();
}
}
# Charger uniquement le groupe "heavy"
php bin/console doctrine:fixtures:load --group=heavy
# Charger un groupe specifique sans purger
php bin/console doctrine:fixtures:load --group=performance --append
Integration avec les tests fonctionnels
Les fixtures sont essentielles pour les tests. Voici un pattern pour les charger dans vos tests fonctionnels :
<?php
namespace App\Tests\Functional;
use App\DataFixtures\CategoryFixtures;
use App\DataFixtures\ProductFixtures;
use Doctrine\Common\DataFixtures\Executor\ORMExecutor;
use Doctrine\Common\DataFixtures\Loader;
use Doctrine\Common\DataFixtures\Purger\ORMPurger;
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
class ProductControllerTest extends WebTestCase
{
protected function setUp(): void
{
parent::setUp();
self::bootKernel();
$em = static::getContainer()
->get('doctrine')
->getManager();
$loader = new Loader();
$loader->addFixture(new CategoryFixtures());
$loader->addFixture(new ProductFixtures());
$purger = new ORMPurger($em);
$executor = new ORMExecutor($em, $purger);
$executor->execute($loader->getFixtures());
}
public function testListProducts(): void
{
$client = static::createClient();
$client->request('GET', '/api/products');
$this->assertResponseIsSuccessful();
$data = json_decode(
$client->getResponse()->getContent(),
true
);
$this->assertCount(3, $data);
}
}
Pour aller plus loin avec la generation de donnees aleatoires, decouvrez notre guide Faker pour Symfony. Et pour gerer l'evolution de votre schema de base, consultez l'article sur les methodes de repository Doctrine.
Strategies de purge
Doctrine Fixtures propose deux strategies de purge avant le chargement :
| Strategie | Commande | Description |
|---|---|---|
| DELETE | --purge-with-truncate absent | Execute des DELETE FROM - plus lent mais respecte les FK |
| TRUNCATE | --purge-with-truncate | Execute des TRUNCATE TABLE - plus rapide, reset les auto-increment |
# Purge avec TRUNCATE (plus rapide, recommande pour les tests)
php bin/console doctrine:fixtures:load --purge-with-truncate --no-interaction
Bonnes pratiques
- Une fixture par entite - gardez les fixtures organisees et lisibles.
- Constantes pour les references - utilisez des constantes de classe pour les cles de reference afin d'eviter les fautes de frappe.
- Flush par batch - pour les grosses fixtures, flushez tous les 500 objets et appelez
$manager->clear()pour liberer la memoire. - Donnees realistes - des donnees realistes aident a detecter les bugs d'affichage et d'encodage (caracteres speciaux, noms longs, etc.).
- Ne jamais utiliser en production - les fixtures purgent la base. Elles ne doivent jamais etre disponibles en prod.
Conclusion
Les fixtures Doctrine sont un outil simple mais puissant pour gerer vos donnees de developpement et de test. Avec les references, les groupes et les dependances, vous pouvez creer des jeux de donnees complexes et coherents, reproductibles sur toutes les machines de l'equipe.
Vous avez besoin d'un accompagnement pour structurer votre projet Symfony ? Consultez mes services, les tarifs, ou contactez-moi directement pour en discuter.
