doctrine/data-fixtures
Doctrine Data Fixtures provides a simple way to define, manage, and execute fixture classes for loading sample or test data into Doctrine ORM or ODM. Useful for seeding databases, repeatable test setups, and development environments with consistent data.
Install the package:
composer require doctrine/data-fixtures
Create a fixture class (e.g., database/fixtures/UserFixture.php):
namespace Database\Fixtures;
use Doctrine\Bundle\FixturesBundle\Fixture;
use Doctrine\Persistence\ObjectManager;
use App\Models\User;
class UserFixture extends Fixture
{
public function load(ObjectManager $manager)
{
$user = new User();
$user->name = 'John Doe';
$user->email = 'john@example.com';
$manager->persist($user);
$this->addReference('user', $user); // Reference for later use
$manager->flush();
}
}
Run fixtures via Artisan:
php artisan doctrine:fixtures:load
(Note: Requires doctrine/doctrine-bundle for Laravel integration. See below for setup.)
UserFixture to populate a users table with test data.PostFixture):
$post = new Post();
$post->title = 'Hello World';
$post->user = $this->getReference('user'); // Reuse the referenced user
doctrine/doctrine-bundle (Recommended)composer require doctrine/doctrine-bundle
php artisan vendor:publish --provider="Doctrine\Bundle\DoctrineBundle\DoctrineBundle" --tag="orm_mapping"
config/packages/doctrine.yaml to enable fixtures:
doctrine:
orm:
auto_generate_proxy_classes: true
mappings:
App:
is_bundle: false
type: attribute
dir: "%kernel.project_dir%/src/Entity"
prefix: "App\Entity"
alias: App
// app/Console/Commands/LoadFixtures.php
namespace App\Console\Commands;
use Doctrine\Bundle\FixturesBundle\Command\LoadDataFixturesDoctrineCommand;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
class LoadFixtures extends LoadDataFixturesDoctrineCommand
{
protected function getFixturesDir(): string
{
return __DIR__ . '/../../database/fixtures';
}
}
Register the command in app/Console/Kernel.php:
protected $commands = [
Commands\LoadFixtures::class,
];
Create Fixtures:
UserFixture, RoleFixture, PermissionFixture).addReference() to link entities across fixtures.Load Fixtures:
php artisan doctrine:fixtures:load
php artisan doctrine:fixtures:load --fixtures=UserFixture
Order Dependencies:
OrderedFixtureInterface to control load order:
use Doctrine\Bundle\FixturesBundle\Fixture\LoadedFixtureInterface;
class UserFixture extends Fixture implements OrderedFixtureInterface
{
public function getOrder(): int
{
return 1; // Load before RoleFixture (order = 2)
}
}
Purge Data Before Loading:
ORMExecutor with purging:
php artisan doctrine:fixtures:load --purge-with-truncate
config/packages/doctrine.yaml:
doctrine:
orm:
auto_generate_proxy_classes: true
fixtures:
purge_mode: 'truncate' # or 'delete'
Inject services (e.g., Hasher, Mailer) into fixtures:
use Symfony\Component\DependencyInjection\ContainerAwareInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
class UserFixture extends Fixture implements ContainerAwareInterface
{
private ContainerInterface $container;
public function setContainer(ContainerInterface $container = null)
{
$this->container = $container;
}
public function load(ObjectManager $manager)
{
$hasher = $this->container->get('hasher');
$user = new User();
$user->password = $hasher->hash('password123');
$manager->persist($user);
}
}
Use Laravel’s config or environment variables:
public function load(ObjectManager $manager)
{
$adminEmail = config('fixtures.admin_email');
$admin = new User(['email' => $adminEmail, 'role' => 'admin']);
$manager->persist($admin);
}
Test fixtures without persisting data:
php artisan doctrine:fixtures:load --dry-run
(Requires DryRunORMExecutor from v2.2.0+.)
getReference() only after the entity is persisted. Avoid bidirectional references in the same fixture.purge-with-truncate drops constraints/triggers. Use purge-with-delete for safer cleanup.config/packages/doctrine.yaml:
doctrine:
orm:
fixtures:
purge_mode: 'delete' # Safer for production-like environments
$manager->getConnection()->beginTransaction();
try {
$this->load($manager);
$manager->getConnection()->commit();
} catch (\Exception $e) {
$manager->getConnection()->rollBack();
throw $e;
}
Run fixtures with debug output:
php artisan doctrine:fixtures:load -vv
Use getOrder() to debug load sequences:
class UserFixture implements OrderedFixtureInterface
{
public function getOrder(): int
{
return 1; // Lower numbers load first
}
}
Dump references to verify they’re set correctly:
public function load(ObjectManager $manager)
{
$user = $this->getReference('user');
if (!$user) {
throw new \RuntimeException('User reference not found!');
}
dump($user); // Debug output
}
Reduce database round-trips by persisting entities in batches:
public function load(ObjectManager $manager)
{
$users = [];
for ($i = 0; $i < 1000; $i++) {
$users[] = new User(['name' => "User $i"]);
}
$manager->persist($users);
$manager->flush();
}
Skip Doctrine events (e.g., prePersist) for bulk operations:
$manager->getEventManager()->removeAllEventListeners();
Extend ExecutorInterface for custom logic (e.g., logging, validation):
use Doctrine\Common\DataFixtures\Executor\ExecutorInterface;
use Doctrine\Common\DataFixtures\Executor\ORMExecutor;
class LoggingExecutor implements ExecutorInterface
{
private ORMExecutor $executor;
public function __construct(ORMExecutor $executor)
{
$this->executor = $executor;
}
public function execute(ExecutorInterface $fixture, bool $purgeMode = false)
{
\Log::info("Loading fixture: " . get_class($fixture));
return $this->executor->execute($fixture, $purgeMode);
}
}
Implement PurgerInterface for database-specific cleanup:
use Doctrine\Common\DataFixtures\Purger\PurgerInterface;
class CustomPurger implements PurgerInterface
{
public function purge(ObjectManager $manager): void
{
$conn = $manager->getConnection();
$conn->executeStatement('TRUNCATE TABLE users RESTART ID
How can I help you explore Laravel packages today?