Skip to content

Commit

Permalink
Fixtures: support --fixtures again
Browse files Browse the repository at this point in the history
  • Loading branch information
f3l1x committed Dec 11, 2024
1 parent 242dbe6 commit 86b082e
Show file tree
Hide file tree
Showing 7 changed files with 123 additions and 22 deletions.
14 changes: 1 addition & 13 deletions .docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
```
Expand Down
31 changes: 24 additions & 7 deletions src/Command/LoadDataFixturesCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -38,14 +40,17 @@ 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'
The <info>%command.name%</info> command loads data fixtures from your application:
<info>php %command.full_name%</info>
Fixtures are services that are tagged with <comment>doctrine.fixture.orm</comment>.
You can also optionally specify the path to fixtures with the <info>--fixtures</info> option:
<info>%command.name% --fixtures=db/fixtures/development --fixtures=db/fixtures/staging</info>
If you want to append the fixtures instead of flushing the database first you can use the <comment>--append</comment> option:
Expand All @@ -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);
Expand Down
2 changes: 0 additions & 2 deletions src/DI/FixturesExtension.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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');
}

Expand Down
8 changes: 8 additions & 0 deletions src/Exceptions/LogicalException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<?php declare(strict_types = 1);

namespace Nettrine\Fixtures\Exceptions;

final class LogicalException extends \LogicException
{

}
8 changes: 8 additions & 0 deletions src/Exceptions/RuntimeException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<?php declare(strict_types = 1);

namespace Nettrine\Fixtures\Exceptions;

final class RuntimeException extends \RuntimeException
{

}
38 changes: 38 additions & 0 deletions src/Utils/ConsoleHelper.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
<?php declare(strict_types = 1);

namespace Nettrine\Fixtures\Utils;

class ConsoleHelper
{

public static function bool(mixed $value): bool
{
return filter_var($value, FILTER_VALIDATE_BOOLEAN);
}

public static function stringNull(mixed $value): ?string
{
if ($value === null || $value === '') {
return null;
}

if (!is_scalar($value)) {
return null;
}

return (string) $value;
}

/**
* @return array<string>
*/
public static function arrayString(mixed $value): array
{
if ($value === null || $value === '') {
return [];
}

return is_array($value) ? $value : []; // @phpstan-ignore-line
}

}
44 changes: 44 additions & 0 deletions tests/Cases/DI/FixturesExtension.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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 {
Expand All @@ -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());
}
});

0 comments on commit 86b082e

Please sign in to comment.