Symfony test data and Doctrine fixtures, FK-consistent from your schema
Maintaining a Doctrine fixture class per entity plus the factories to wire them together rots the moment the schema moves, and copying a production dump into your test suite is a GDPR problem waiting to happen. The fix: realistic, foreign-key-consistent Symfony test data generated straight from your schema, data that actually looks real, not "Premium Widget 1" and lorem ipsum. Register one bundle, run doctrine:fixtures:load, and one command fills the whole database.
Free tier, no card. Want to see the output before wiring anything up? Try the login-free sandbox: paste a schema, generate, look at the rows.
More stacks: Laravel test data · SQL test data · Django test data
Install the Symfony bundle for Doctrine fixtures
Install the package and register the bundle, then SeedBase plugs into the fixtures workflow you already run. Pull it in with Composer:
composer require seedbase/seedbase
Register the bundle in config/bundles.php:
<?php
// config/bundles.php
return [
// ...
Seedbase\Symfony\SeedbaseBundle::class => ['all' => true],
];
Then configure your token, project and seed in config/packages/seedbase.yaml. The token resolves from the environment, so no secret lives in the repo:
# config/packages/seedbase.yaml
seedbase:
token: '%env(SEEDBASE_TOKEN)%'
api_url: 'https://seedbase.dev/api/v1'
project: '%env(SEEDBASE_PROJECT)%'
seed: '%env(int:SEEDBASE_SEED)%'
Grab a free API key under Settings, API keys (it looks like dr_sk_...) and put it in SEEDBASE_TOKEN. Create a project by importing your schema (a SQL dump, or a live database URL) at seedbase.dev and use its id as SEEDBASE_PROJECT.
Fill the database with doctrine:fixtures:load
As a DoctrineFixturesBundle alternative for the bulk of your data, you stop writing a fixture class per entity. The bundle ships a single SeedbaseFixtures class that extends the DoctrineFixturesBundle Fixture, so you load it exactly the way you already load fixtures:
php bin/console doctrine:fixtures:load
The autowired SeedbaseFixtures generates and loads foreign-key-consistent data over your Doctrine connection, using the project and seed from your config. Your schema must already exist, your migrations own it. SeedBase reads that schema, generates realistic rows that satisfy every relationship, and inserts them parent-before-child so the load succeeds with constraints on.
Inject the SeedbaseClient in a service or command
Once the bundle is registered, SeedbaseClient is autowirable. Type-hint it in any service, controller or console command and Symfony injects a configured instance, so you can drive generation from your own code: generate a dataset, then download it as SQL.
<?php
// src/Command/SeedCommand.php
namespace App\Command;
use Seedbase\SeedbaseClient;
use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
#[AsCommand(name: 'app:seed')]
final class SeedCommand extends Command
{
public function __construct(private SeedbaseClient $client)
{
parent::__construct();
}
protected function execute(InputInterface $input, OutputInterface $output): int
{
$gen = $this->client->generate('my-project', ['seed' => 42, 'wait' => true]);
$sql = $this->client->download($gen['id'], 'sql');
file_put_contents('var/seed.sql', $sql);
return Command::SUCCESS;
}
}
generate() creates a generation and, with wait => true, blocks until it is ready, so $gen['id'] is the finished generation. download($id, 'sql') returns the INSERT statements as a string, ready to load with doctrine:database:import or pipe into psql in CI. Pass a fixed seed and the run is reproducible.
Realistic, foreign-key-consistent data
The value is the engine, not the thin Symfony adapter. SeedBase resolves foreign keys across tables, so an order that points at a customer always finds a customer that exists, and the rows load with constraints on. It uses realistic distributions and coherent free text, so a description reads like a description and an email reads like an email, instead of "Premium Widget 1" or lorem ipsum your assertions can never trust.
- Foreign keys resolved across tables, with rows inserted parent-before-child.
- Realistic value distributions and coherent free text, not placeholder filler.
- One generation feeds the fixture, an autowired client call or a plain SQL download.
This complements Faker and Doctrine fixtures rather than replacing them. Faker invents one field at a time and a fixture wires the relations by hand; SeedBase invents the whole dataset schema-aware, so the foreign keys line up across hundreds of tables without you writing the wiring. Keep the bespoke fixtures that encode a specific scenario, let SeedBase fill the bulk.
Deterministic and reproducible
Generation is seeded. The same seed produces the same data, every run. That keeps CI reproducible: the same fixture yields the same rows on every machine, so a green build stays green for the right reason. When a test fails, reuse the seed and you reproduce the exact dataset that broke it, instead of chasing a fixture that has since been hand-edited.
Free tier
Generation and SQL or JSON download are free to use, no credit card. You do not need push-to-database or any paid feature to seed your Symfony tests, because the data loads through your own Doctrine connection. Generate once, run doctrine:fixtures:load, and the bundle wires it into the database. Paid plans start at €19/month and add Parquet export, direct database push and higher row limits when you want them.
Try it
There is a runnable example you can clone and run, so you watch a real FK-consistent dataset get loaded through a Doctrine connection. Then point it at your own schema.
php examples/doctrine_seed_demo.php
- Paste a schema and look at the rows in the login-free sandbox, no account needed.
- On Laravel instead of Symfony? See Laravel test data, same engine, same FK-consistency.
- Prefer raw
INSERTstatements over a fixture? See SQL test data. - Coming from Faker? SeedBase vs Faker shows where each one fits.
- Read the docs for the Symfony bundle and the
SeedbaseClientservice.
Stop hand-writing Doctrine fixtures.
Generate a foreign-key-consistent dataset once and seed your Symfony database with one command. Free tier, no card.
Create a free accountMore: login-free sandbox · Laravel test data · SQL test data · vs Faker · Fixtures hub · Docs
Symfony test data and Doctrine fixtures: FAQ
How do I generate Symfony test data with SeedBase?
Run composer require seedbase/seedbase, register Seedbase\Symfony\SeedbaseBundle in config/bundles.php, set your token, project and seed in config/packages/seedbase.yaml, then run php bin/console doctrine:fixtures:load. The bundled SeedbaseFixtures class fills your existing schema with foreign-key-consistent rows over your Doctrine connection.
How do I replace hand-written Doctrine fixtures?
You stop writing a fixture class per entity. The single SeedbaseFixtures class reads your schema and fills the database foreign-key-consistent, with realistic distributions, so an order that points at a customer actually finds that customer and the values look real instead of Premium Widget 1 or lorem ipsum. Your migrations stay the source of truth, SeedBase only fills the schema they define.
Does it work with DoctrineFixturesBundle and Faker?
Yes. SeedbaseFixtures extends the DoctrineFixturesBundle Fixture class and loads via doctrine:fixtures:load, so it sits alongside any fixtures you keep. It complements Faker and Doctrine fixtures rather than replacing them: Faker invents one field at a time, SeedBase invents the whole dataset schema-aware and foreign-key-consistent across tables.
How do I inject the SeedbaseClient in a service or command?
Once the bundle is registered, SeedbaseClient is autowirable. Type-hint it in any service, controller or console command constructor and Symfony injects a configured instance. Call $client->generate($projectId, ['seed' => 42, 'wait' => true]) to produce a generation, then $client->download($gen['id'], 'sql') to get the SQL.
How do I configure the bundle?
In config/packages/seedbase.yaml set token to '%env(SEEDBASE_TOKEN)%', api_url to 'https://seedbase.dev/api/v1', project to '%env(SEEDBASE_PROJECT)%' and seed to '%env(int:SEEDBASE_SEED)%'. The token resolves from the config or the SEEDBASE_TOKEN environment variable, so secrets stay out of the repo.
Is the generated data deterministic for CI?
Yes, per seed. The same seed produces the same data every run, so CI is reproducible: the same fixture yields the same rows on every machine, and a failing test can be reproduced exactly by reusing the seed.
Is it free?
Yes. Generation and JSON download are part of the free tier with no credit card, so you can pull a foreign-key-consistent dataset and load it through your own Doctrine connection at no cost. Paid plans start at 19 euros per month and add Parquet export, direct database push and higher row limits, but you do not need any of that to seed your tests.