diff --git a/.docs/README.md b/.docs/README.md index 19350f3..82c97b0 100755 --- a/.docs/README.md +++ b/.docs/README.md @@ -51,6 +51,27 @@ nettrine.migrations: directories: array versionsOrganization: customTemplate: + allOrNothing: + + migrationFactory: + logger: + connection: + manager: +``` + +**Multiple databases** + +```php +$this->configurator->addDynamicParameters([ + 'env' => getenv(), +]); +``` + +```neon +nettrine.migrations: + directories: + App\Migrations: %appDir%/migrations + connection: %env.DATABASE_CONNECTION% ``` ## Usage @@ -136,4 +157,3 @@ Since this moment when you type `bin/console`, there'll be registered commands f > [!TIP] > Take a look at more examples in [contributte/doctrine](https://github.com/contributte/doctrine/tree/master/.docs). - diff --git a/ruleset.xml b/ruleset.xml index 697077a..62feb13 100644 --- a/ruleset.xml +++ b/ruleset.xml @@ -1,7 +1,9 @@ - + + + diff --git a/src/DI/Helpers/SmartStatement.php b/src/DI/Helpers/SmartStatement.php new file mode 100644 index 0000000..1567213 --- /dev/null +++ b/src/DI/Helpers/SmartStatement.php @@ -0,0 +1,22 @@ +required()->assert(fn ($input) => str_starts_with($input, '@') || class_exists($input) || interface_exists($input)), + Expect::type(Statement::class)->required(), + ); + return Expect::structure([ 'table' => Expect::string('doctrine_migrations'), 'column' => Expect::string('version'), @@ -44,6 +51,10 @@ public function getConfigSchema(): Schema ), 'customTemplate' => Expect::string(), 'allOrNothing' => Expect::bool(false), + 'logger' => (clone $expectService), + 'migrationFactory' => (clone $expectService), + 'connection' => Expect::string(), + 'manager' => Expect::string(), ]); } @@ -66,6 +77,14 @@ public function loadConfiguration(): void ->addSetup('setMetadataStorageConfiguration', [$storage]) ->addSetup('setAllOrNothing', [$config->allOrNothing]); + if ($config->connection !== null) { + $configuration->addSetup('setConnectionName', [$config->connection]); + } + + if ($config->manager !== null) { + $configuration->addSetup('setEntityManagerName', [$config->manager]); + } + foreach ($config->directories as $namespace => $directory) { $configuration->addSetup('addMigrationsDirectory', [$namespace, $directory]); } @@ -76,15 +95,13 @@ public function loadConfiguration(): void $configuration->addSetup('setMigrationsAreOrganizedByYearAndMonth'); } - $builder->addDefinition($this->prefix('migrationFactory')) - ->setFactory(DbalMigrationFactory::class); - - $builder->addDefinition($this->prefix('dependencyFactory')) - ->setType(NativeDependencyFactory::class) - ->setFactory($this->prefix('@internal.dependencyFactory::create')); + $dependencyFactory = $builder->addDefinition($this->prefix('dependencyFactory')) + ->setType(DependencyFactory::class) + ->setFactory(DependencyFactoryCreator::class . '::create'); - $builder->addDefinition($this->prefix('internal.dependencyFactory')) - ->setFactory(DependencyFactory::class); + if ($config->migrationFactory !== null) { + $dependencyFactory->addSetup('setService', [MigrationFactory::class, SmartStatement::from($config->migrationFactory)]); + } // Register commands diff --git a/src/DependencyFactory.php b/src/DependencyFactory.php deleted file mode 100644 index 8d3ff08..0000000 --- a/src/DependencyFactory.php +++ /dev/null @@ -1,51 +0,0 @@ -entityManager !== null) { - $dependencyFactory = NativeDependencyFactory::fromEntityManager(new ExistingConfiguration($this->configuration), new ExistingEntityManager($this->entityManager), $this->logger); - } elseif ($this->connection !== null) { - $dependencyFactory = NativeDependencyFactory::fromConnection(new ExistingConfiguration($this->configuration), new ExistingConnection($this->connection), $this->logger); - } else { - throw new ServiceCreationException( - sprintf( - 'Either service of type %s or %s needs to be registered for Doctrine migrations to work properly', - Connection::class, - EntityManagerInterface::class - ) - ); - } - - $dependencyFactory->setService(MigrationFactory::class, $this->migrationFactory); - - return $dependencyFactory; - } - -} diff --git a/src/DependencyFactoryCreator.php b/src/DependencyFactoryCreator.php new file mode 100644 index 0000000..402d1d0 --- /dev/null +++ b/src/DependencyFactoryCreator.php @@ -0,0 +1,76 @@ +dependencyFactory->getConnection(), + $this->dependencyFactory->getLogger() + ); + + assert($migration instanceof AbstractMigration); + + return $migration; + } + + }; + + $dependencyFactory->setService(MigrationFactory::class, new MigrationFactoryDecorator($container, $migrationFactory)); + + return $dependencyFactory; + } + +} diff --git a/src/Exceptions/LogicalException.php b/src/Exceptions/LogicalException.php new file mode 100644 index 0000000..8d046cb --- /dev/null +++ b/src/Exceptions/LogicalException.php @@ -0,0 +1,8 @@ +migrationFactory->createVersion($migrationClassName); + + // Call setContainer + if ($instance instanceof ContainerAwareInterface) { + $instance->setContainer($this->container); + } + + // Allow to use inject<> + $this->container->callInjects($instance); + + return $instance; + } + +} diff --git a/src/Version/DbalMigrationFactory.php b/src/Version/DbalMigrationFactory.php deleted file mode 100644 index 4be8de5..0000000 --- a/src/Version/DbalMigrationFactory.php +++ /dev/null @@ -1,42 +0,0 @@ -container = $container; - $this->connection = $connection; - $this->logger = $logger ?? new NullLogger(); - } - - public function createVersion(string $migrationClassName): AbstractMigration - { - $migration = new $migrationClassName( - $this->connection, - $this->logger - ); - - assert($migration instanceof AbstractMigration); - - $this->container->callInjects($migration); - - return $migration; - } - -} diff --git a/tests/Cases/DI/MigrationsExtension.phpt b/tests/Cases/DI/MigrationsExtension.phpt index 3160dba..3b0ed0a 100644 --- a/tests/Cases/DI/MigrationsExtension.phpt +++ b/tests/Cases/DI/MigrationsExtension.phpt @@ -10,26 +10,27 @@ use Doctrine\Migrations\DependencyFactory; use Doctrine\Migrations\Tools\Console\Command\DoctrineCommand; use Nette\DI\Compiler; use Nettrine\Migrations\DI\MigrationsExtension; -use Nettrine\Migrations\Version\DbalMigrationFactory; +use Nettrine\Migrations\Exceptions\LogicalException; +use Nettrine\Migrations\Migration\MigrationFactoryDecorator; use Symfony\Component\Console\Application; use Tester\Assert; require_once __DIR__ . '/../../bootstrap.php'; +// OK Toolkit::test(function (): void { $container = ContainerBuilder::of() ->withCompiler(function (Compiler $compiler): void { $compiler->addExtension('migrations', new MigrationsExtension()); $compiler->addConfig(Neonkit::load(' - parameters: - appDir: /root migrations: directories: - App\Domain: %appDir%/migrations + App\Domain: /root/migrations services: - Symfony\Component\Console\Application - Doctrine\DBAL\Driver\Mysqli\Driver - Doctrine\DBAL\Connection([]) + - Tests\Mocks\DummyConnectionRegistry ')); }) ->build(); @@ -45,5 +46,23 @@ Toolkit::test(function (): void { /** @var DependencyFactory $dependencyFactory */ $dependencyFactory = $container->getByType(DependencyFactory::class); Assert::type(DependencyFactory::class, $dependencyFactory); - Assert::type(DbalMigrationFactory::class, $dependencyFactory->getMigrationFactory()); + Assert::type(MigrationFactoryDecorator::class, $dependencyFactory->getMigrationFactory()); +}); + +// No ConnectionRegistry or ManagerRegistry +Toolkit::test(function (): void { + Assert::exception(function (): void { + $container = ContainerBuilder::of() + ->withCompiler(function (Compiler $compiler): void { + $compiler->addExtension('migrations', new MigrationsExtension()); + $compiler->addConfig(Neonkit::load(' + migrations: + directories: + App\Domain: /root/migrations + ')); + }) + ->build(); + + $container->getByType(DependencyFactory::class); + }, LogicalException::class, 'You must provide either ManagerRegistry or ConnectionRegistry.'); }); diff --git a/tests/Mocks/DummyConnectionRegistry.php b/tests/Mocks/DummyConnectionRegistry.php new file mode 100644 index 0000000..f18e5ee --- /dev/null +++ b/tests/Mocks/DummyConnectionRegistry.php @@ -0,0 +1,43 @@ +connection; + } + + /** + * @return array + */ + public function getConnections(): array + { + return ['default' => $this->connection]; + } + + /** + * @return string[] + */ + public function getConnectionNames(): array + { + return ['default']; + } + +}