diff --git a/src/Command/LoadDataFixturesDoctrineCommand.php b/src/Command/LoadDataFixturesDoctrineCommand.php index b5e876e..9742a24 100644 --- a/src/Command/LoadDataFixturesDoctrineCommand.php +++ b/src/Command/LoadDataFixturesDoctrineCommand.php @@ -44,6 +44,7 @@ protected function configure(): void ->setName('doctrine:fixtures:load') ->setDescription('Load data fixtures to your database') ->addOption('append', null, InputOption::VALUE_NONE, 'Append the data fixtures instead of deleting all data from the database first.') + ->addOption('dry-run', null, InputOption::VALUE_NONE, 'Load the fixtures as a dry run.') ->addOption('group', null, InputOption::VALUE_IS_ARRAY | InputOption::VALUE_REQUIRED, 'Only load fixtures that belong to this group') ->addOption('em', null, InputOption::VALUE_REQUIRED, 'The entity manager to use for this command.') ->addOption('purger', null, InputOption::VALUE_REQUIRED, 'The purger to use for this command', 'default') @@ -68,6 +69,10 @@ protected function configure(): void To execute only fixtures that live in a certain group, use: php %command.full_name% --group=group1 + + You can also load the fixtures as a --dry-run: + + php %command.full_name% --dry-run EOT); } @@ -79,7 +84,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int $em = $this->getDoctrine()->getManager($input->getOption('em')); assert($em instanceof EntityManagerInterface); - if (! $input->getOption('append')) { + if (! $input->getOption('dry-run') && ! $input->getOption('append')) { if (! $ui->confirm(sprintf('Careful, database "%s" will be purged. Do you want to continue?', $em->getConnection()->getDatabase()), ! $input->isInteractive())) { return 0; } @@ -117,6 +122,12 @@ protected function execute(InputInterface $input, OutputInterface $output): int $input->getOption('purge-exclusions'), $input->getOption('purge-with-truncate'), ); + + if ($input->getOption('dry-run')) { + $ui->text(' (dry-run)'); + $em->getConnection()->beginTransaction(); + } + $executor = new ORMExecutor($em, $purger); $executor->setLogger(new class ($ui) extends AbstractLogger { public function __construct(private SymfonyStyle $ui) @@ -138,6 +149,10 @@ public function __invoke(string $message): void $executor->execute($fixtures, $input->getOption('append')); + if ($input->getOption('dry-run')) { + $em->getConnection()->rollBack(); + } + return 0; } } diff --git a/tests/IntegrationTest.php b/tests/IntegrationTest.php index ca480b4..f9a0068 100644 --- a/tests/IntegrationTest.php +++ b/tests/IntegrationTest.php @@ -292,7 +292,15 @@ public function testRunCommandWithDefaultPurger(): void $kernel->boot(); $container = $kernel->getContainer(); - $em = $this->createConfiguredMock(ForwardCompatibleEntityManager::class, ['getConnection' => $this->createMock(Connection::class), 'getEventManager' => $this->createMock(EventManager::class)]); + $connection = $this->createMock(Connection::class); + $connection + ->expects(self::never()) + ->method('beginTransaction'); + $connection + ->expects(self::never()) + ->method('rollBack'); + + $em = $this->createConfiguredMock(ForwardCompatibleEntityManager::class, ['getConnection' => $connection, 'getEventManager' => $this->createMock(EventManager::class)]); $registry = $this->createMock(ManagerRegistry::class); $registry ->expects(self::once()) @@ -339,7 +347,15 @@ public function testRunCommandWithPurgeExclusions(): void $kernel->boot(); $container = $kernel->getContainer(); - $em = $this->createConfiguredMock(ForwardCompatibleEntityManager::class, ['getConnection' => $this->createMock(Connection::class), 'getEventManager' => $this->createMock(EventManager::class)]); + $connection = $this->createMock(Connection::class); + $connection + ->expects(self::never()) + ->method('beginTransaction'); + $connection + ->expects(self::never()) + ->method('rollBack'); + + $em = $this->createConfiguredMock(ForwardCompatibleEntityManager::class, ['getConnection' => $connection, 'getEventManager' => $this->createMock(EventManager::class)]); $registry = $this->createMock(ManagerRegistry::class); $registry ->expects(self::once()) @@ -389,7 +405,15 @@ public function testRunCommandWithCustomPurgerAndCustomEntityManager(): void $kernel->boot(); $container = $kernel->getContainer(); - $em = $this->createConfiguredMock(ForwardCompatibleEntityManager::class, ['getConnection' => $this->createMock(Connection::class), 'getEventManager' => $this->createMock(EventManager::class)]); + $connection = $this->createMock(Connection::class); + $connection + ->expects(self::never()) + ->method('beginTransaction'); + $connection + ->expects(self::never()) + ->method('rollBack'); + + $em = $this->createConfiguredMock(ForwardCompatibleEntityManager::class, ['getConnection' => $connection, 'getEventManager' => $this->createMock(EventManager::class)]); $registry = $this->createMock(ManagerRegistry::class); $registry ->expects(self::once()) @@ -436,7 +460,15 @@ public function testRunCommandWithPurgeMode(): void $kernel->boot(); $container = $kernel->getContainer(); - $em = $this->createConfiguredMock(ForwardCompatibleEntityManager::class, ['getConnection' => $this->createMock(Connection::class), 'getEventManager' => $this->createMock(EventManager::class)]); + $connection = $this->createMock(Connection::class); + $connection + ->expects(self::never()) + ->method('beginTransaction'); + $connection + ->expects(self::never()) + ->method('rollBack'); + + $em = $this->createConfiguredMock(ForwardCompatibleEntityManager::class, ['getConnection' => $connection, 'getEventManager' => $this->createMock(EventManager::class)]); $registry = $this->createMock(ManagerRegistry::class); $registry ->expects(self::once()) @@ -459,6 +491,62 @@ public function testRunCommandWithPurgeMode(): void $tester = new CommandTester($command); $tester->execute(['--purge-with-truncate' => true], ['interactive' => false]); } + + public function testRunCommandWithDryRunOption(): void + { + $kernel = new IntegrationTestKernel('dev', true); + $kernel->addServices(static function (ContainerBuilder $c): void { + // has a "staging" group via the getGroups() method + $c->autowire(OtherFixtures::class) + ->addTag(FixturesCompilerPass::FIXTURE_TAG); + + // no getGroups() method + $c->autowire(WithDependenciesFixtures::class) + ->addTag(FixturesCompilerPass::FIXTURE_TAG); + + $c->getDefinition('doctrine') + ->setPublic(true) + ->setSynthetic(true); + + $c->setAlias('test.doctrine.fixtures.purger.orm_purger_factory', new Alias('doctrine.fixtures.purger.orm_purger_factory', true)); + + $c->setAlias('test.doctrine.fixtures_load_command', new Alias('doctrine.fixtures_load_command', true)); + }); + $kernel->boot(); + $container = $kernel->getContainer(); + + $connection = $this->createMock(Connection::class); + $connection + ->expects(self::once()) + ->method('beginTransaction'); + $connection + ->expects(self::once()) + ->method('rollBack'); + + $em = $this->createConfiguredMock(ForwardCompatibleEntityManager::class, ['getConnection' => $connection, 'getEventManager' => $this->createMock(EventManager::class)]); + $registry = $this->createMock(ManagerRegistry::class); + $registry + ->expects(self::once()) + ->method('getManager') + ->with(null) + ->willReturn($em); + $container->set('doctrine', $registry); + + $purgerFactory = $this->createMock(PurgerFactory::class); + $purger = $this->createMock(ORMPurgerInterface::class); + $purgerFactory + ->expects(self::once()) + ->method('createForEntityManager') + ->with(null, $em, []) + ->willReturn($purger); + $container->set('test.doctrine.fixtures.purger.orm_purger_factory', $purgerFactory); + + $command = $container->get('test.doctrine.fixtures_load_command'); + $this->assertInstanceOf(LoadDataFixturesDoctrineCommand::class, $command); + $tester = new CommandTester($command); + $tester->execute(['--dry-run' => true], ['interactive' => false]); + $this->assertStringContainsString('(dry-run)', $tester->getDisplay()); + } } interface ForwardCompatibleEntityManager extends EntityManagerInterface