diff --git a/.docs/README.md b/.docs/README.md index 3676b88..e4f2d37 100755 --- a/.docs/README.md +++ b/.docs/README.md @@ -46,22 +46,10 @@ nettrine.fixtures: Type `bin/console` in your terminal and there should be a `doctrine:fixtures` command group. -The **doctrine:fixtures:load** command loads data fixtures from your configuration by default: - ``` bin/console doctrine:fixtures:load -``` - -If you want to append the fixtures instead of first flushing the database you can use the **--append** option: - -``` bin/console doctrine:fixtures:load --append -``` - -By default `Doctrine Fixtures` uses `DELETE` statements to drop the existing rows from -the database. If you want to use a `TRUNCATE` statement instead, you can use the **--purge-with-truncate** flag: - -``` +bin/console doctrine:fixtures:load --fixtures=db/fixtures/development bin/console doctrine:fixtures:load --purge-with-truncate ``` diff --git a/src/Command/LoadDataFixturesCommand.php b/src/Command/LoadDataFixturesCommand.php index 901e6b6..f7e4353 100644 --- a/src/Command/LoadDataFixturesCommand.php +++ b/src/Command/LoadDataFixturesCommand.php @@ -6,7 +6,9 @@ use Doctrine\Common\DataFixtures\Purger\ORMPurger; use Doctrine\ORM\EntityManagerInterface; use Doctrine\Persistence\ManagerRegistry; +use Nettrine\Fixtures\Exceptions\LogicalException; use Nettrine\Fixtures\Loader\FixturesLoader; +use Nettrine\Fixtures\Utils\ConsoleHelper; use Psr\Log\AbstractLogger; use Stringable; use Symfony\Component\Console\Attribute\AsCommand; @@ -38,6 +40,7 @@ protected function configure(): void $this ->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('fixtures', null, InputOption::VALUE_OPTIONAL | InputOption::VALUE_IS_ARRAY, 'File or directory to load data fixtures from.') ->addOption('em', null, InputOption::VALUE_REQUIRED, 'The entity manager to use for this command.') ->addOption('purge-with-truncate', null, InputOption::VALUE_NONE, 'Purge data by using a database-level TRUNCATE statement') ->setHelp(<<<'EOT' @@ -45,7 +48,9 @@ protected function configure(): void php %command.full_name% - Fixtures are services that are tagged with doctrine.fixture.orm. + You can also optionally specify the path to fixtures with the --fixtures option: + + %command.name% --fixtures=db/fixtures/development --fixtures=db/fixtures/staging If you want to append the fixtures instead of flushing the database first you can use the --append option: @@ -63,25 +68,37 @@ protected function execute(InputInterface $input, OutputInterface $output): int { $ui = new SymfonyStyle($input, $output); - $inputAppend = filter_var($input->getOption('append'), FILTER_VALIDATE_BOOLEAN); - $inputEm = is_string($input->getOption('em')) && $input->getOption('em') !== '' ? $input->getOption('em') : null; - $inputTruncate = filter_var($input->getOption('purge-with-truncate'), FILTER_VALIDATE_BOOLEAN); + $inputAppend = ConsoleHelper::bool($input->getOption('append')); + $inputEm = ConsoleHelper::stringNull($input->getOption('em')); + $inputTruncate = ConsoleHelper::bool($input->getOption('purge-with-truncate')); + $inputFixtures = ConsoleHelper::arrayString($input->getOption('fixtures')); $em = $this->managerRegistry->getManager($inputEm); assert($em instanceof EntityManagerInterface); + // Ask user to confirm purging database if (!$inputAppend) { if (!$ui->confirm(sprintf('Careful, database "%s" will be purged. Do you want to continue?', $em->getConnection()->getDatabase()), !$input->isInteractive())) { return 0; } } - $this->fixturesLoader->load(); + // Load fixtures from given paths + if ($inputFixtures === []) { + $this->fixturesLoader->load(); + } else { + $this->fixturesLoader->loadPaths($inputFixtures); + } + $fixtures = $this->fixturesLoader->getFixtures(); if ($fixtures === []) { - $ui->error('Could not find any fixture services to load.'); + $paths = $this->fixturesLoader->getPaths(); - return 1; + if ($paths === []) { + throw new LogicalException('Could not find any fixtures to load.'); + } else { + throw new LogicalException(sprintf('Could not find any fixtures to load in paths: %s', implode(', ', $paths))); + } } $purger = new ORMPurger($em); diff --git a/src/DI/FixturesExtension.php b/src/DI/FixturesExtension.php index dd04d78..4b07937 100644 --- a/src/DI/FixturesExtension.php +++ b/src/DI/FixturesExtension.php @@ -3,7 +3,6 @@ namespace Nettrine\Fixtures\DI; use Nette\DI\CompilerExtension; -use Nette\DI\Extensions\InjectExtension; use Nette\Schema\Expect; use Nette\Schema\Schema; use Nettrine\Fixtures\Command\LoadDataFixturesCommand; @@ -33,7 +32,6 @@ public function loadConfiguration(): void $builder->addDefinition($this->prefix('loadDataFixturesCommand')) ->setFactory(LoadDataFixturesCommand::class) - ->addTag(InjectExtension::TagInject, true) ->addTag('console.command', 'doctrine:fixtures:load'); } diff --git a/src/Exceptions/LogicalException.php b/src/Exceptions/LogicalException.php new file mode 100644 index 0000000..151d39a --- /dev/null +++ b/src/Exceptions/LogicalException.php @@ -0,0 +1,8 @@ + + */ + public static function arrayString(mixed $value): array + { + if ($value === null || $value === '') { + return []; + } + + return is_array($value) ? $value : []; // @phpstan-ignore-line + } + +} diff --git a/tests/Cases/DI/FixturesExtension.phpt b/tests/Cases/DI/FixturesExtension.phpt index 4466ae1..544f161 100644 --- a/tests/Cases/DI/FixturesExtension.phpt +++ b/tests/Cases/DI/FixturesExtension.phpt @@ -4,20 +4,25 @@ namespace Tests\Cases\Unit\DI; use Contributte\Tester\Toolkit; use Contributte\Tester\Utils\ContainerBuilder; +use Doctrine\ORM\EntityManagerInterface; use Doctrine\Persistence\ManagerRegistry; use Mockery; +use Mockery\MockInterface; use Nette\DI\Compiler; use Nette\DI\Container; use Nette\DI\Definitions\Statement; use Nettrine\Fixtures\Command\LoadDataFixturesCommand; use Nettrine\Fixtures\DI\FixturesExtension; +use Nettrine\Fixtures\Exceptions\RuntimeException; use Nettrine\Fixtures\Loader\FixturesLoader; +use Symfony\Component\Console\Tester\CommandTester; use Tester\Assert; use Tests\Mocks\ContainerFixture; use Tests\Toolkit\Tests; require_once __DIR__ . '/../../bootstrap.php'; +// Minimal configuration Toolkit::test(function (): void { $container = ContainerBuilder::of() ->withCompiler(function (Compiler $compiler): void { @@ -57,6 +62,7 @@ Toolkit::test(function (): void { Assert::type(LoadDataFixturesCommand::class, $command); }); +// Load by paths Toolkit::test(function (): void { $container = ContainerBuilder::of() ->withCompiler(function (Compiler $compiler): void { @@ -80,3 +86,41 @@ Toolkit::test(function (): void { $containerFixture = $loader->getFixture(ContainerFixture::class); Assert::type(Container::class, $containerFixture->getContainer()); }); + +// Load by paths +Toolkit::test(function (): void { + $container = ContainerBuilder::of() + ->withCompiler(function (Compiler $compiler): void { + $compiler->getContainerBuilder() + ->addDefinition('managerRegistry') + ->setType(ManagerRegistry::class) + ->setFactory(new Statement(Mockery::class . '::mock', [ManagerRegistry::class])); + + $compiler->addExtension('fixtures', new FixturesExtension()); + })->build(); + + /** @var FixturesLoader $loader */ + $loader = $container->getByType(FixturesLoader::class); + Assert::count(0, $loader->getFixtures()); + + $em = Mockery::mock(EntityManagerInterface::class); + $em->shouldReceive('getEventManager')->andThrows(new RuntimeException('Not implemented', 999)); + + /** @var MockInterface $managerRegistry */ + $managerRegistry = $container->getByType(ManagerRegistry::class); + $managerRegistry->shouldReceive('getManager')->andReturn($em); + + /** @var LoadDataFixturesCommand $loadDataFixtureCommand */ + $loadDataFixtureCommand = $container->getByType(LoadDataFixturesCommand::class); + + try { + $commandTester = new CommandTester($loadDataFixtureCommand); + $commandTester->execute([ + '--append' => true, + '--fixtures' => [Tests::FIXTURES_PATH], + ]); + } catch (RuntimeException $e) { + Assert::equal(999, $e->getCode()); + Assert::count(1, $loader->getFixtures()); + } +});