DoctrineFixturesBundle

repository·4.3.x·Indexed 25 days ago

https://github.com/doctrine/doctrinefixturesbundle

A Symfony bundle that integrates the Doctrine Data Fixtures library, allowing developers to programmatically load sample or test data into the Doctrine ORM. It provides the `doctrine:fixtures:load` CLI command to execute fixtures, with support for database purging, dry runs, fixture grouping, and dependency injection for accessing application services.

Tokens
4.7K
Snippets
8
Records
17
Agent score
82%

What's inside DoctrineFixturesBundle

  1. Share objects between fixtures using object references

    4.3.x

    When splitting fixtures into multiple files, you can reuse ORM entities across files using object references.

    1. In the source fixture, use $this->addReference('name', $object) to store an entity.
    2. In the dependent fixture, use $this->getReference('name', EntityClass::class) to retrieve it.

    Note: This only works for ORM entities.

    // src/DataFixtures/UserFixtures.php
    class UserFixtures extends Fixture
    {
        public const ADMIN_USER_REFERENCE = 'admin-user';
    
        public function load(ObjectManager $manager): void
        {
            $userAdmin = new User('admin', 'pass_1234');
            $manager->persist($userAdmin);
            $manager->flush();
    
            $this->addReference(self::ADMIN_USER_REFERENCE, $userAdmin);
        }
    }
    
    // src/DataFixtures/GroupFixtures.php
    class GroupFixtures extends Fixture
    {
        public function load(ObjectManager $manager): void
        {
            $userGroup = new Group('administrators');
            // returns the User object created in UserFixtures
            $userGroup->addUser($this->getReference(UserFixtures::ADMIN_USER_REFERENCE, User::class));
    
            $manager->persist($userGroup);
            $manager->flush();
        }
    }
  2. Access services in fixtures via dependency injection

    4.3.x

    Since fixture classes are registered as services in Symfony, you can use standard dependency injection to access application services (like password hashers) within your load() method.

    // src/DataFixtures/AppFixtures.php
    use Symfony\Component\PasswordHasher\Hasher\UserPasswordHasherInterface;
    
    class AppFixtures extends Fixture
    {
        private UserPasswordHasherInterface $hasher;
    
        public function __construct(UserPasswordHasherInterface $hasher)
        {
            $this->hasher = $hasher;
        }
    
        // ...
        public function load(ObjectManager $manager): void
        {
            $user = new User();
            $user->setUsername('admin');
    
            $password = $this->hasher->hashPassword($user, 'pass_1234');
            $user->setPassword($password);
    
            $manager->persist($user);
            $manager->flush();
        }
    }
  3. Migrate to DoctrineFixturesBundle 3.0

    4.3.x

    When upgrading to version 3.0, several architectural changes were introduced regarding fixture registration and dependency management:

    Registering Fixtures as Services

    Automatic loading of fixtures from a directory (e.g., AppBundle\DataFixtures\ORM) has been removed. You must now register fixture classes as services and tag them with doctrine.fixture.orm.

    If you are using Symfony 3.3+ with default service configuration, this happens automatically if your fixture classes extend Doctrine\Bundle\FixturesBundle\Fixture or implement Doctrine\Bundle\FixturesBundle\ORMFixtureInterface.

    Dependency Injection in Fixtures

    The base Fixture class no longer implements ContainerAwareInterface, meaning the $this->container property is no longer available. It is recommended to use constructor dependency injection instead of manually implementing ContainerAwareInterface.

    Implementing Dependencies

    The base Fixture class no longer implements DependentFixtureInterface. If your fixture requires a getDependencies() method, you must explicitly implement Doctrine\Common\DataFixtures\DependentFixtureInterface.

    # src/AppBundle/Resources/config/dataFixture.yml
    services:
      _defaults:
        tags: ['doctrine.fixture.orm']
        autowire: true # if you need dependency injection, see next bullet point
    
      AppBundle\DataFixtures\ORM\:
        resource: '../../DataFixtures/ORM/*'
    class MyFixture extends Fixture
    {
    +     private $someService;
    
    +     public function __construct(SomeService $someService)
    +     {
    +         $this->someService = $someService;
    +     }
    
        public function load(ObjectManager $manager)
        {
    -         $this->container->get('some_service')->someMethod();
    +         $this->someService->someMethod();
        }
    }
    + use Doctrine\Common\DataFixtures\DependentFixtureInterface;
    
    - class MyFixture extends Fixture
    + class MyFixture extends Fixture implements DependentFixtureInterface
  4. Write data fixtures

    4.3.x

    Data fixtures are PHP classes where you create and persist objects to the database. To create a fixture, extend the Doctrine\Bundle\FixturesBundle\Fixture class and implement the load(ObjectManager $manager) method. Use the $manager to persist() your entities and flush() to save them to the database.

    // src/DataFixtures/AppFixtures.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
        {
            // create 20 products with random prices
            for ($i = 0; $i < 20; $i++) {
                $product = new Product();
                $product->setName('product '.$i);
                $product->setPrice(mt_rand(10, 100));
                $manager->persist($product);
            }
    
            $manager->flush();
        }
    }
  5. Implement and register a custom Purger

    4.3.x

    If the built-in purging methods are insufficient, you can implement a custom purger and factory.

    1. Implement ORMPurgerInterface for your purging logic.
    2. Implement PurgerFactory to instantiate your purger.
    3. Register the factory in your service container with the tag doctrine.fixtures.purger_factory and assign it an alias.
    4. Use the --purger option in the CLI to select your custom purger by its alias.
    // src/Purger/CustomPurger.php
    namespace App\Purger;
    
    use Doctrine\Common\DataFixtures\Purger\ORMPurgerInterface;
    use Doctrine\ORM\EntityManagerInterface;
    
    class CustomPurger implements ORMPurgerInterface
    {
        private EntityManagerInterface $entityManager;
    
        public function setEntityManager(EntityManagerInterface $em): void
        {
            $this->entityManager = $em;
        }
    
        public function purge(): void
        {
            // ... custom logic
        }
    }
    
    // src/Purger/CustomPurgerFactory.php
    namespace App\Purger;
    
    use Doctrine\Bundle\FixturesBundle\Purger\PurgerFactory;
    use Doctrine\Common\DataFixtures\Purger\PurgerInterface;
    use Doctrine\ORM\EntityManagerInterface;
    
    class CustomPurgerFactory implements PurgerFactory
    {
        public function createForEntityManager(?string $emName, EntityManagerInterface $em, array $excluded = [], bool $purgeWithTruncate = false) : PurgerInterface
        {
            return new CustomPurger();
        }
    }
  6. Migrate to DoctrineFixturesBundle 4.0

    4.3.x

    When upgrading to version 4.0, be aware of the following breaking changes:

    • Type Declarations: Strict type declarations have been added throughout the bundle. Any custom classes that extend or implement bundle types must be updated to match the new signatures.
    • Final Classes: Several classes are now marked as final. You should use composition instead of inheritance if you previously extended these classes.
    • Mandatory ManagerRegistry: The constructor for Doctrine\Bundle\FixturesBundle\Command\LoadDataFixturesDoctrineCommand now requires a ManagerRegistry instance.
  7. Run fixtures in Dry Run mode

    4.3.x
    The --dry-run option allows you to simulate the execution of your fixtures. It processes the fixtures and handles logging/output as usual, but all persisting operations are skipped and no changes are made to the database. This is useful for safely inspecting fixture behavior.
  8. Configure purging behavior when loading fixtures

    4.3.x

    By default, doctrine:fixtures:load purges existing data using DELETE FROM table statements. You can customize this behavior using CLI options or by implementing a custom purger.

    CLI Options

    • Truncate instead of Delete: Use --purge-with-truncate to use TRUNCATE table statements.
    • Exclude tables: Use --purge-exclusions to prevent specific tables from being purged (useful for semi-static data). You can pass this flag multiple times to exclude multiple tables.

    Custom Purger Implementation

    To implement a custom purging strategy, create a class implementing ORMPurgerInterface and a factory implementing PurgerFactory.

  9. Load fixtures from a custom directory

    4.3.x

    By default, fixtures are loaded from src/DataFixtures. To use a different directory (e.g., fixtures/):

    1. Update Autoloading: Add a PSR-4 entry for the new directory in your composer.json under autoload-dev.
    2. Run Composer: Execute composer dump-autoload.
    3. Configure Dependency Injection: Register the new directory in your Symfony service configuration so that fixtures are properly autowired and autoconfigured.

    Note: Changing this directory does not affect the Symfony MakerBundle (make:fixtures) command, which will continue to use the default src/DataFixtures path.

    // composer.json
    "autoload-dev": {
        "psr-4": {
            "DataFixtures\": "fixtures/"
        }
    }
    # config/services.yaml
    services:
        DataFixtures\:
            resource: '../fixtures'
    // config/services.php
    namespace Symfony\Component\DependencyInjection\Loader\Configurator;
    
    return function(ContainerConfigurator $container): void {
        $services = $container->services()
            ->defaults()
                ->autowire()
                ->autoconfigure();
    
        $services->load('DataFixtures\\', '../fixtures');
    };
  10. Organize and execute specific fixture groups

    4.3.x

    By default, all fixtures are executed. To run only a subset, you can use groups.

    Using FixtureGroupInterface

    Implement Doctrine\Bundle\FixturesBundle\FixtureGroupInterface and define the groups in a static getGroups() method:

    class UserFixtures extends Fixture implements FixtureGroupInterface
    {
        public static function getGroups(): array
        {
            return ['group1', 'group2'];
        }
    }

    Running groups via CLI

    Use the --group option to specify which group(s) to load. You can also load a single fixture by its class name, as the loader automatically adds the short class name as a group.

    # Execute a specific group
    $ php bin/console doctrine:fixtures:load --group=group1
    
    # Execute multiple groups
    $ php bin/console doctrine:fixtures:load --group=group1 --group=group2
    
    # Execute a single fixture by its class name
    $ php bin/console doctrine:fixtures:load --group=UserFixtures