From fa26f5a39a859586bb47e2c0ec9e2f5cee3d46ba Mon Sep 17 00:00:00 2001 From: Mark Story Date: Wed, 25 Sep 2024 23:43:13 -0400 Subject: [PATCH 1/3] Add and use a ShimAdapter Update the internals of Manager and Environment to use Migrations interfaces for Seeds. This pushes the compatibility shims down a layer and should continue to give the same backwards compatibility. Next I'll build a BaseSeed that is all migrations internals. --- src/Migration/Environment.php | 8 +- src/Migration/Manager.php | 75 ++---- src/SeedInterface.php | 34 +-- src/Shim/SeedAdapter.php | 303 +++++++++++++++++++++++ tests/TestCase/Migration/ManagerTest.php | 8 +- 5 files changed, 347 insertions(+), 81 deletions(-) create mode 100644 src/Shim/SeedAdapter.php diff --git a/src/Migration/Environment.php b/src/Migration/Environment.php index 3ce62820..0ae06d4b 100644 --- a/src/Migration/Environment.php +++ b/src/Migration/Environment.php @@ -13,8 +13,8 @@ use Migrations\Db\Adapter\AdapterFactory; use Migrations\Db\Adapter\AdapterInterface; use Migrations\Db\Adapter\PhinxAdapter; +use Migrations\SeedInterface; use Phinx\Migration\MigrationInterface; -use Phinx\Seed\SeedInterface; use RuntimeException; class Environment @@ -131,15 +131,13 @@ public function executeMigration(MigrationInterface $migration, string $directio /** * Executes the specified seeder on this environment. * - * @param \Phinx\Seed\SeedInterface $seed Seed + * @param \Migrations\Seed\SeedInterface $seed Seed * @return void */ public function executeSeed(SeedInterface $seed): void { $adapter = $this->getAdapter(); - $phinxAdapter = new PhinxAdapter($adapter); - - $seed->setAdapter($phinxAdapter); + $seed->setAdapter($adapter); if (method_exists($seed, SeedInterface::INIT)) { $seed->{SeedInterface::INIT}(); } diff --git a/src/Migration/Manager.php b/src/Migration/Manager.php index b6a1021c..b2c30c72 100644 --- a/src/Migration/Manager.php +++ b/src/Migration/Manager.php @@ -14,17 +14,16 @@ use Exception; use InvalidArgumentException; use Migrations\Config\ConfigInterface; +use Migrations\SeedInterface; use Migrations\Shim\OutputAdapter; +use Migrations\Shim\SeedAdapter; use Phinx\Migration\AbstractMigration; use Phinx\Migration\MigrationInterface; -use Phinx\Seed\AbstractSeed; -use Phinx\Seed\SeedInterface; +use Phinx\Seed\SeedInterface as PhinxSeedInterface; use Phinx\Util\Util; use Psr\Container\ContainerInterface; use RuntimeException; use Symfony\Component\Console\Input\ArrayInput; -use Symfony\Component\Console\Input\InputDefinition; -use Symfony\Component\Console\Input\InputOption; class Manager { @@ -53,7 +52,7 @@ class Manager protected ?array $migrations = null; /** - * @var \Phinx\Seed\SeedInterface[]|null + * @var \Migrations\SeedInterface[]|null */ protected ?array $seeds = null; @@ -475,7 +474,7 @@ public function executeMigration(MigrationInterface $migration, string $directio /** * Execute a seeder against the specified environment. * - * @param \Phinx\Seed\SeedInterface $seed Seed + * @param \Migrations\Seed\SeedInterface $seed Seed * @return void */ public function executeSeed(SeedInterface $seed): void @@ -523,7 +522,7 @@ protected function printMigrationStatus(MigrationInterface $migration, string $s /** * Print Seed Status * - * @param \Phinx\Seed\SeedInterface $seed Seed + * @param \Migrations\Seed\SeedInterface $seed Seed * @param string $status Status of the seed * @param string|null $duration Duration the seed took the be executed * @return void @@ -901,7 +900,7 @@ protected function getMigrationFiles(): array /** * Sets the database seeders. * - * @param \Phinx\Seed\SeedInterface[] $seeds Seeders + * @param \Migrations\SeedInterface[] $seeds Seeders * @return $this */ public function setSeeds(array $seeds) @@ -914,8 +913,8 @@ public function setSeeds(array $seeds) /** * Get seed dependencies instances from seed dependency array * - * @param \Phinx\Seed\SeedInterface $seed Seed - * @return \Phinx\Seed\SeedInterface[] + * @param \Migrations\SeedInterface $seed Seed + * @return \Migrations\SeedInterface[] */ protected function getSeedDependenciesInstances(SeedInterface $seed): array { @@ -924,8 +923,9 @@ protected function getSeedDependenciesInstances(SeedInterface $seed): array if (!empty($dependencies) && !empty($this->seeds)) { foreach ($dependencies as $dependency) { foreach ($this->seeds as $seed) { - if (get_class($seed) === $dependency) { - $dependenciesInstances[get_class($seed)] = $seed; + $name = $seed->getName(); + if ($name === $dependency) { + $dependenciesInstances[$name] = $seed; } } } @@ -937,14 +937,15 @@ protected function getSeedDependenciesInstances(SeedInterface $seed): array /** * Order seeds by dependencies * - * @param \Phinx\Seed\SeedInterface[] $seeds Seeds - * @return \Phinx\Seed\SeedInterface[] + * @param \Migrations\SeedInterface[] $seeds Seeds + * @return \Migrations\SeedInterface[] */ protected function orderSeedsByDependencies(array $seeds): array { $orderedSeeds = []; foreach ($seeds as $seed) { - $orderedSeeds[get_class($seed)] = $seed; + $name = $seed->getName(); + $orderedSeeds[$name] = $seed; $dependencies = $this->getSeedDependenciesInstances($seed); if (!empty($dependencies)) { $orderedSeeds = array_merge($this->orderSeedsByDependencies($dependencies), $orderedSeeds); @@ -958,7 +959,7 @@ protected function orderSeedsByDependencies(array $seeds): array * Gets an array of database seeders. * * @throws \InvalidArgumentException - * @return \Phinx\Seed\SeedInterface[] + * @return \Migrations\SeedInterface[] */ public function getSeeds(): array { @@ -967,25 +968,11 @@ public function getSeeds(): array // filter the files to only get the ones that match our naming scheme $fileNames = []; - /** @var \Phinx\Seed\SeedInterface[] $seeds */ + /** @var \Migrations\SeedInterface[] $seeds */ $seeds = []; $config = $this->getConfig(); - // TODO Subset config and pass forward. - // TODO move this to the migration/phinx shim - $optionDef = new InputDefinition([ - new InputOption('plugin', mode: InputOption::VALUE_OPTIONAL, default: ''), - new InputOption('connection', mode: InputOption::VALUE_OPTIONAL, default: ''), - new InputOption('source', mode: InputOption::VALUE_OPTIONAL, default: ''), - ]); - // TODO move this to the migration/phinx shim - $input = new ArrayInput([ - '--plugin' => $config['plugin'] ?? null, - '--source' => $config['source'] ?? null, - '--connection' => $config->getConnection(), - ], $optionDef); - // TODO move this to the migration/phinx shim - $output = new OutputAdapter($this->io); + $io = $this->getIo(); foreach ($phpFiles as $filePath) { if (Util::isValidSeedFileName(basename($filePath))) { @@ -1011,18 +998,13 @@ public function getSeeds(): array } else { $seed = new $class(); } - // TODO Replace with with setIo and setConfig - $seed->setEnvironment('default'); - $seed->setInput($input); - $seed->setOutput($output); - - if (!($seed instanceof AbstractSeed)) { - throw new InvalidArgumentException(sprintf( - 'The class "%s" in file "%s" must extend \Phinx\Seed\AbstractSeed', - $class, - $filePath - )); + // Shim phinx seeds so that the rest of migrations + // can be isolated from phinx. + if ($seed instanceof PhinxSeedInterface) { + $seed = new SeedAdapter($seed); } + $seed->setIo($io); + $seed->setConfig($config); $seeds[$class] = $seed; } @@ -1036,13 +1018,6 @@ public function getSeeds(): array return []; } - // TODO remove this - foreach ($this->seeds as $instance) { - if (isset($input) && $instance instanceof AbstractSeed) { - $instance->setInput($input); - } - } - return $this->seeds; } diff --git a/src/SeedInterface.php b/src/SeedInterface.php index d9788122..df94c178 100644 --- a/src/SeedInterface.php +++ b/src/SeedInterface.php @@ -79,9 +79,9 @@ public function getIo(): ?ConsoleIo; /** * Gets the config. * - * @return \Migrations\Config\ConfigInterface + * @return \Migrations\Config\ConfigInterface|null */ - public function getConfig(): ConfigInterface; + public function getConfig(): ?ConfigInterface; /** * Sets the config. @@ -91,26 +91,6 @@ public function getConfig(): ConfigInterface; */ public function setConfig(ConfigInterface $config); - /** - * Gets the input object to be used in migration object - * - * A new InputInteface will be generated each time `getOutput` is called. - * - * @return \Symfony\Component\Console\Input\InputInterface - * @deprecated 4.5.0 Use getIo() instead. - */ - public function getInput(): InputInterface; - - /** - * Gets the output object to be used in migration object - * - * A new OutputInteface will be generated each time `getOutput` is called. - * - * @return \Symfony\Component\Console\Output\OutputInterface - * @deprecated 4.5.0 Use getIo() instead. - */ - public function getOutput(): OutputInterface; - /** * Gets the name. * @@ -195,4 +175,14 @@ public function table(string $tableName, array $options): Table; * @return bool */ public function shouldExecute(): bool; + + /** + * Gives the ability to a seeder to call another seeder. + * This is particularly useful if you need to run the seeders of your applications in a specific sequences, + * for instance to respect foreign key constraints. + * + * @param string $seeder Name of the seeder to call from the current seed + * @return void + */ + public function call(string $seeder): void; } diff --git a/src/Shim/SeedAdapter.php b/src/Shim/SeedAdapter.php new file mode 100644 index 00000000..fe0250eb --- /dev/null +++ b/src/Shim/SeedAdapter.php @@ -0,0 +1,303 @@ +seed, PhinxSeedInterface::INIT)) { + $this->seed->{PhinxSeedInterface::INIT}(); + } + } + + /** + * {@inheritDoc} + */ + public function run(): void + { + $this->seed->run(); + } + + /** + * {@inheritDoc} + */ + public function getDependencies(): array + { + return $this->seed->getDependencies(); + } + + /** + * Sets the database adapter. + * + * @param \Migrations\Db\Adapter\AdapterInterface $adapter Database Adapter + * @return $this + */ + public function setAdapter(AdapterInterface $adapter) + { + $phinxAdapter = new PhinxAdapter($adapter); + $this->seed->setAdapter($phinxAdapter); + $this->adapter = $adapter; + + return $this; + } + + /** + * Gets the database adapter. + * + * @return \Migrations\Db\Adapter\AdapterInterface + */ + public function getAdapter(): AdapterInterface + { + return $this->adapter; + } + + /** + * {@inheritDoc} + */ + public function setIo(ConsoleIo $io) + { + $this->io = $io; + $this->seed->setOutput(new OutputAdapter($io)); + + return $this; + } + + /** + * {@inheritDoc} + */ + public function getIo(): ?ConsoleIo + { + return $this->io; + } + + /** + * Gets the config. + * + * @return ?\Migrations\Config\ConfigInterface + */ + public function getConfig(): ?ConfigInterface + { + return $this->config; + } + + /** + * Sets the config. + * + * @param \Migrations\Config\ConfigInterface $config Configuration Object + * @return $this + */ + public function setConfig(ConfigInterface $config) + { + $optionDef = new InputDefinition([ + new InputOption('plugin', mode: InputOption::VALUE_OPTIONAL, default: ''), + new InputOption('connection', mode: InputOption::VALUE_OPTIONAL, default: ''), + new InputOption('source', mode: InputOption::VALUE_OPTIONAL, default: ''), + ]); + $input = new ArrayInput([ + '--plugin' => $config['plugin'] ?? null, + '--source' => $config['source'] ?? null, + '--connection' => $config->getConnection(), + ], $optionDef); + + $this->seed->setInput($input); + $this->config = $config; + + return $this; + } + + /** + * Gets the name. + * + * @return string + */ + public function getName(): string + { + return $this->seed->getName(); + } + + /** + * Executes a SQL statement and returns the number of affected rows. + * + * @param string $sql SQL + * @param array $params parameters to use for prepared query + * @return int + */ + public function execute(string $sql, array $params = []): int + { + return $this->seed->execute($sql, $params); + } + + /** + * Executes a SQL statement. + * + * The return type depends on the underlying adapter being used. To improve + * IDE auto-completion possibility, you can overwrite the query method + * phpDoc in your (typically custom abstract parent) seed class, where + * you can set the return type by the adapter in your current use. + * + * @param string $sql SQL + * @param array $params parameters to use for prepared query + * @return mixed + */ + public function query(string $sql, array $params = []): mixed + { + return $this->query($sql, $params); + } + + /** + * Executes a query and returns only one row as an array. + * + * @param string $sql SQL + * @return array|false + */ + public function fetchRow(string $sql): array|false + { + return $this->fetchRow($sql); + } + + /** + * Executes a query and returns an array of rows. + * + * @param string $sql SQL + * @return array + */ + public function fetchAll(string $sql): array + { + return $this->fetchAll($sql); + } + + /** + * Insert data into a table. + * + * @param string $tableName Table name + * @param array $data Data + * @return void + */ + public function insert(string $tableName, array $data): void + { + $this->insert($tableName, $data); + } + + /** + * Checks to see if a table exists. + * + * @param string $tableName Table name + * @return bool + */ + public function hasTable(string $tableName): bool + { + return $this->hasTable($tableName); + } + + /** + * Returns an instance of the \Table class. + * + * You can use this class to create and manipulate tables. + * + * @param string $tableName Table name + * @param array $options Options + * @return \Migrations\Db\Table + */ + public function table(string $tableName, array $options): Table + { + throw new RuntimeException('Not implemented'); + } + + /** + * Checks to see if the seed should be executed. + * + * Returns true by default. + * + * You can use this to prevent a seed from executing. + * + * @return bool + */ + public function shouldExecute(): bool + { + return $this->seed->shouldExecute(); + } + + /** + * Gives the ability to a seeder to call another seeder. + * This is particularly useful if you need to run the seeders of your applications in a specific sequences, + * for instance to respect foreign key constraints. + * + * @param string $seeder Name of the seeder to call from the current seed + * @return void + */ + public function call(string $seeder): void + { + throw new RuntimeException('Not implemented'); + } +} diff --git a/tests/TestCase/Migration/ManagerTest.php b/tests/TestCase/Migration/ManagerTest.php index 764b9a5b..b739ace8 100644 --- a/tests/TestCase/Migration/ManagerTest.php +++ b/tests/TestCase/Migration/ManagerTest.php @@ -2187,9 +2187,9 @@ public function testExecuteANonExistentSeedWorksAsExpected(): void public function testOrderSeeds(): void { $seeds = array_values($this->manager->getSeeds()); - $this->assertInstanceOf('UserSeeder', $seeds[0]); - $this->assertInstanceOf('GSeeder', $seeds[1]); - $this->assertInstanceOf('PostSeeder', $seeds[2]); + $this->assertEquals('UserSeeder', $seeds[0]->getName()); + $this->assertEquals('GSeeder', $seeds[1]->getName()); + $this->assertEquals('PostSeeder', $seeds[2]->getName()); } public function testSeedWillNotBeExecuted(): void @@ -2228,7 +2228,7 @@ public function testGettingIo(): void $this->assertInstanceOf(OutputAdapter::class, $migration->getOutput()); } foreach ($seeds as $seed) { - $this->assertInstanceOf(OutputAdapter::class, $seed->getOutput()); + $this->assertInstanceOf(ConsoleIo::class, $seed->getIo()); } } From 8e5faa49c02f2a064bed7384d026510e6182af7c Mon Sep 17 00:00:00 2001 From: Mark Story Date: Thu, 26 Sep 2024 23:18:49 -0400 Subject: [PATCH 2/3] Fix test and phpcs --- src/Migration/Environment.php | 2 +- src/Migration/Manager.php | 7 ++++--- src/SeedInterface.php | 2 -- src/Shim/SeedAdapter.php | 4 ++++ tests/TestCase/Migration/EnvironmentTest.php | 6 ++++-- 5 files changed, 13 insertions(+), 8 deletions(-) diff --git a/src/Migration/Environment.php b/src/Migration/Environment.php index 0ae06d4b..25bc8a5f 100644 --- a/src/Migration/Environment.php +++ b/src/Migration/Environment.php @@ -131,7 +131,7 @@ public function executeMigration(MigrationInterface $migration, string $directio /** * Executes the specified seeder on this environment. * - * @param \Migrations\Seed\SeedInterface $seed Seed + * @param \Migrations\SeedInterface $seed Seed * @return void */ public function executeSeed(SeedInterface $seed): void diff --git a/src/Migration/Manager.php b/src/Migration/Manager.php index b2c30c72..a50e2a93 100644 --- a/src/Migration/Manager.php +++ b/src/Migration/Manager.php @@ -474,7 +474,7 @@ public function executeMigration(MigrationInterface $migration, string $directio /** * Execute a seeder against the specified environment. * - * @param \Migrations\Seed\SeedInterface $seed Seed + * @param \Migrations\SeedInterface $seed Seed * @return void */ public function executeSeed(SeedInterface $seed): void @@ -522,7 +522,7 @@ protected function printMigrationStatus(MigrationInterface $migration, string $s /** * Print Seed Status * - * @param \Migrations\Seed\SeedInterface $seed Seed + * @param \Migrations\SeedInterface $seed Seed * @param string $status Status of the seed * @param string|null $duration Duration the seed took the be executed * @return void @@ -992,7 +992,7 @@ public function getSeeds(): array } // instantiate it - /** @var \Phinx\Seed\AbstractSeed $seed */ + /** @var \Phinx\Seed\AbstractSeed|\Migrations\SeedInterface $seed */ if (isset($this->container)) { $seed = $this->container->get($class); } else { @@ -1003,6 +1003,7 @@ public function getSeeds(): array if ($seed instanceof PhinxSeedInterface) { $seed = new SeedAdapter($seed); } + /** @var \Migrations\SeedInterface $seed */ $seed->setIo($io); $seed->setConfig($config); diff --git a/src/SeedInterface.php b/src/SeedInterface.php index df94c178..cd2e3243 100644 --- a/src/SeedInterface.php +++ b/src/SeedInterface.php @@ -12,8 +12,6 @@ use Migrations\Config\ConfigInterface; use Migrations\Db\Adapter\AdapterInterface; use Migrations\Db\Table; -use Symfony\Component\Console\Input\InputInterface; -use Symfony\Component\Console\Output\OutputInterface; /** * Seed interface diff --git a/src/Shim/SeedAdapter.php b/src/Shim/SeedAdapter.php index fe0250eb..e2f946be 100644 --- a/src/Shim/SeedAdapter.php +++ b/src/Shim/SeedAdapter.php @@ -119,6 +119,10 @@ public function setAdapter(AdapterInterface $adapter) */ public function getAdapter(): AdapterInterface { + if (!$this->adapter) { + throw new RuntimeException('Cannot call getAdapter() until after setAdapter().'); + } + return $this->adapter; } diff --git a/tests/TestCase/Migration/EnvironmentTest.php b/tests/TestCase/Migration/EnvironmentTest.php index 6d24b088..0778242e 100644 --- a/tests/TestCase/Migration/EnvironmentTest.php +++ b/tests/TestCase/Migration/EnvironmentTest.php @@ -8,6 +8,7 @@ use Migrations\Db\Adapter\AdapterWrapper; use Migrations\Db\Adapter\PdoAdapter; use Migrations\Migration\Environment; +use Migrations\Shim\SeedAdapter; use Phinx\Migration\AbstractMigration; use Phinx\Migration\MigrationInterface; use Phinx\Seed\AbstractSeed; @@ -314,7 +315,6 @@ public function testExecuteSeedInit() $this->environment->setAdapter($adapterStub); - // up $seed = new class ('mockenv', 20110301080000) extends AbstractSeed { public bool $initExecuted = false; public bool $runExecuted = false; @@ -330,7 +330,9 @@ public function run(): void } }; - $this->environment->executeSeed($seed); + $seedWrapper = new SeedAdapter($seed); + $this->environment->executeSeed($seedWrapper); + $this->assertTrue($seed->initExecuted); $this->assertTrue($seed->runExecuted); } From 2c55d5cf12987417af829b8cde781c22e6b1ea4f Mon Sep 17 00:00:00 2001 From: Mark Story Date: Fri, 27 Sep 2024 11:40:23 -0400 Subject: [PATCH 3/3] Fix more phpcs --- src/Shim/SeedAdapter.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Shim/SeedAdapter.php b/src/Shim/SeedAdapter.php index e2f946be..e404bc39 100644 --- a/src/Shim/SeedAdapter.php +++ b/src/Shim/SeedAdapter.php @@ -61,7 +61,7 @@ class SeedAdapter implements SeedInterface /** * Constructor * - * @param \Phinx\Seed\SeedInterface; + * @param \Phinx\Seed\SeedInterface $seed The seed being decorated */ public function __construct( private PhinxSeedInterface $seed