Skip to content

Commit

Permalink
Extract a factory for migration config/manager
Browse files Browse the repository at this point in the history
Looks like we're going to need this in most migrations commands.
  • Loading branch information
markstory committed Mar 17, 2024
1 parent a986a0f commit 2706a4c
Show file tree
Hide file tree
Showing 4 changed files with 150 additions and 157 deletions.
86 changes: 8 additions & 78 deletions src/Command/MigrateCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
use Migrations\Config\Config;
use Migrations\Config\ConfigInterface;
use Migrations\Migration\Manager;
use Migrations\Migration\ManagerFactory;
use Throwable;

/**
Expand Down Expand Up @@ -92,83 +93,6 @@ public function buildOptionParser(ConsoleOptionParser $parser): ConsoleOptionPar
return $parser;
}

/**
* Generate a configuration object for the migrations operation.
*
* @param \Cake\Console\Arguments $args The console arguments
* @return \Migrations\Config\Config The generated config instance.
*/
protected function getConfig(Arguments $args): Config
{
$folder = (string)$args->getOption('source');

// Get the filepath for migrations and seeds(not implemented yet)
$dir = ROOT . DS . 'config' . DS . $folder;
if (defined('CONFIG')) {
$dir = CONFIG . $folder;
}
$plugin = $args->getOption('plugin');
if ($plugin && is_string($plugin)) {
$dir = Plugin::path($plugin) . 'config' . DS . $folder;
}

// Get the phinxlog table name. Plugins have separate migration history.
// The names and separate table history is something we could change in the future.
$table = 'phinxlog';
if ($plugin && is_string($plugin)) {
$prefix = Inflector::underscore($plugin) . '_';
$prefix = str_replace(['\\', '/', '.'], '_', $prefix);
$table = $prefix . $table;
}
$templatePath = dirname(__DIR__) . DS . 'templates' . DS;
$connectionName = (string)$args->getOption('connection');

// TODO this all needs to go away. But first Environment and Manager need to work
// with Cake's ConnectionManager.
$connectionConfig = ConnectionManager::getConfig($connectionName);
if (!$connectionConfig) {
throw new StopException("Could not find connection `{$connectionName}`");
}

/** @var array<string, string> $connectionConfig */
$adapter = $connectionConfig['scheme'] ?? null;
$adapterConfig = [
'adapter' => $adapter,
'connection' => $connectionName,
'database' => $connectionConfig['database'],
'migration_table' => $table,
'dryrun' => $args->getOption('dry-run'),
];

$configData = [
'paths' => [
'migrations' => $dir,
],
'templates' => [
'file' => $templatePath . 'Phinx/create.php.template',
],
'migration_base_class' => 'Migrations\AbstractMigration',
'environment' => $adapterConfig,
// TODO do we want to support the DI container in migrations?
];

return new Config($configData);
}

/**
* Get the migration manager for the current CLI options and application configuration.
*
* @param \Cake\Console\Arguments $args The command arguments.
* @param \Cake\Console\ConsoleIo $io The command io.
* @return \Migrations\Migration\Manager
*/
protected function getManager(Arguments $args, ConsoleIo $io): Manager
{
$config = $this->getConfig($args);

return new Manager($config, $io);
}

/**
* Execute the command.
*
Expand Down Expand Up @@ -201,7 +125,13 @@ protected function executeMigrations(Arguments $args, ConsoleIo $io): ?int
$date = $args->getOption('date');
$fake = (bool)$args->getOption('fake');

$manager = $this->getManager($args, $io);
$factory = new ManagerFactory([
'plugin' => $args->getOption('plugin'),
'source' => $args->getOption('source'),
'connection' => $args->getOption('connection'),
'dry-run' => $args->getOption('dry-run'),
]);
$manager = $factory->createManager($io);
$config = $manager->getConfig();

$versionOrder = $config->getVersionOrder();
Expand Down
88 changes: 10 additions & 78 deletions src/Command/StatusCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
use Migrations\Config\Config;
use Migrations\Config\ConfigInterface;
use Migrations\Migration\Manager;
use Migrations\Migration\ManagerFactory;

/**
* Status command for built in backend
Expand Down Expand Up @@ -89,83 +90,6 @@ public function buildOptionParser(ConsoleOptionParser $parser): ConsoleOptionPar
return $parser;
}

/**
* Generate a configuration object for the migrations operation.
*
* @param \Cake\Console\Arguments $args The console arguments
* @return \Migrations\Config\Config The generated config instance.
*/
protected function getConfig(Arguments $args): Config
{
$folder = (string)$args->getOption('source');

// Get the filepath for migrations and seeds(not implemented yet)
$dir = ROOT . '/config/' . $folder;
if (defined('CONFIG')) {
$dir = CONFIG . $folder;
}
$plugin = $args->getOption('plugin');
if ($plugin && is_string($plugin)) {
$dir = Plugin::path($plugin) . 'config/' . $folder;
}

// Get the phinxlog table name. Plugins have separate migration history.
// The names and separate table history is something we could change in the future.
$table = 'phinxlog';
if ($plugin && is_string($plugin)) {
$prefix = Inflector::underscore($plugin) . '_';
$prefix = str_replace(['\\', '/', '.'], '_', $prefix);
$table = $prefix . $table;
}
$templatePath = dirname(__DIR__) . DS . 'templates' . DS;
$connectionName = (string)$args->getOption('connection');

// TODO this all needs to go away. But first Environment and Manager need to work
// with Cake's ConnectionManager.
$connectionConfig = ConnectionManager::getConfig($connectionName);
if (!$connectionConfig) {
throw new StopException("Could not find connection `{$connectionName}`");
}

/** @var array<string, string> $connectionConfig */
$adapter = $connectionConfig['scheme'] ?? null;
$adapterConfig = [
'adapter' => $adapter,
'connection' => $connectionName,
'database' => $connectionConfig['database'],
'migration_table' => $table,
'dryrun' => $args->getOption('dry-run'),
];

$configData = [
'paths' => [
'migrations' => $dir,
],
'templates' => [
'file' => $templatePath . 'Phinx/create.php.template',
],
'migration_base_class' => 'Migrations\AbstractMigration',
'environment' => $adapterConfig,
// TODO do we want to support the DI container in migrations?
];

return new Config($configData);
}

/**
* Get the migration manager for the current CLI options and application configuration.
*
* @param \Cake\Console\Arguments $args The command arguments.
* @param \Cake\Console\ConsoleIo $io The command io.
* @return \Migrations\Migration\Manager
*/
protected function getManager(Arguments $args, ConsoleIo $io): Manager
{
$config = $this->getConfig($args);

return new Manager($config, $io);
}

/**
* Execute the command.
*
Expand All @@ -177,7 +101,15 @@ public function execute(Arguments $args, ConsoleIo $io): ?int
{
/** @var string|null $format */
$format = $args->getOption('format');
$migrations = $this->getManager($args, $io)->printStatus($format);

$factory = new ManagerFactory([
'plugin' => $args->getOption('plugin'),
'source' => $args->getOption('source'),
'connection' => $args->getOption('connection'),
'dry-run' => $args->getOption('dry-run'),
]);
$manager = $factory->createManager($io);
$migrations = $manager->printStatus($format);

switch ($format) {
case 'json':
Expand Down
130 changes: 130 additions & 0 deletions src/Migration/ManagerFactory.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
<?php
declare(strict_types=1);

/**
* Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
*
* Licensed under The MIT License
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
* @link https://cakephp.org CakePHP(tm) Project
* @license https://www.opensource.org/licenses/mit-license.php MIT License
*/
namespace Migrations\Migration;

use Cake\Console\ConsoleIo;
use Cake\Core\Plugin;
use Cake\Datasource\ConnectionManager;
use Cake\Utility\Inflector;
use Migrations\Config\Config;
use Migrations\Config\ConfigInterface;
use RuntimeException;

/**
* Factory for Config and Manager
*
* Used by Console commands.
*
* @internal
*/
class ManagerFactory
{
/**
* Constructor
*
* ## Options
*
* - source - The directory in app/config that migrations should be read from.
* - plugin - The plugin name that migrations are being run on.
* - connection - The connection name.
* - dry-run - Whether or not dry-run mode should be enabled.
*
* @param array $options The command line options for creating config/manager.
*/
public function __construct(protected array $options)
{
}

public function getOption(string $name): mixed
{
if (!isset($this->options[$name])) {
return null;
}

return $this->options[$name];
}

public function createConfig(): ConfigInterface
{
$folder = (string)$this->getOption('source');

// Get the filepath for migrations and seeds(not implemented yet)
$dir = ROOT . DS . 'config' . DS . $folder;
if (defined('CONFIG')) {
$dir = CONFIG . $folder;
}
$plugin = $this->getOption('plugin');
if ($plugin && is_string($plugin)) {
$dir = Plugin::path($plugin) . 'config' . DS . $folder;
}

// Get the phinxlog table name. Plugins have separate migration history.
// The names and separate table history is something we could change in the future.
$table = 'phinxlog';
if ($plugin && is_string($plugin)) {
$prefix = Inflector::underscore($plugin) . '_';
$prefix = str_replace(['\\', '/', '.'], '_', $prefix);
$table = $prefix . $table;
}
$templatePath = dirname(__DIR__) . DS . 'templates' . DS;
$connectionName = (string)$this->getOption('connection');

// TODO this all needs to go away. But first Environment and Manager need to work
// with Cake's ConnectionManager.
$connectionConfig = ConnectionManager::getConfig($connectionName);
if (!$connectionConfig) {
throw new RuntimeException("Could not find connection `{$connectionName}`");
}

/** @var array<string, string> $connectionConfig */
$adapter = $connectionConfig['scheme'] ?? null;
$adapterConfig = [
'adapter' => $adapter,
'connection' => $connectionName,
'database' => $connectionConfig['database'],
'migration_table' => $table,
'dryrun' => $this->getOption('dry-run'),
];

$configData = [
'paths' => [
'migrations' => $dir,
],
'templates' => [
'file' => $templatePath . 'Phinx/create.php.template',
],
'migration_base_class' => 'Migrations\AbstractMigration',
'environment' => $adapterConfig,
// TODO do we want to support the DI container in migrations?
];

return new Config($configData);
}

/**
* Get the migration manager for the current CLI options and application configuration.
*
* @param \Cake\Console\ConsoleIo $io The command io.
* @param \Migrations\Config\ConfigInterface $config A config instance. Providing null will create a new Config
* based on the factory constructor options.
* @return \Migrations\Migration\Manager
*/
public function createManager(ConsoleIo $io, ConfigInterface|null $config = null): Manager
{
$config ??= $this->createConfig();

return new Manager($config, $io);
}
}

3 changes: 2 additions & 1 deletion tests/TestCase/Command/StatusCommandTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
use Cake\Core\Exception\MissingPluginException;
use Cake\Database\Exception\DatabaseException;
use Cake\TestSuite\TestCase;
use RuntimeException;

class StatusCommandTest extends TestCase
{
Expand Down Expand Up @@ -74,7 +75,7 @@ public function testExecutePluginDoesNotExist(): void

public function testExecuteConnectionDoesNotExist(): void
{
$this->expectException(RuntimeException::class);
$this->exec('migrations status -c lolnope');
$this->assertExitError();
}
}

0 comments on commit 2706a4c

Please sign in to comment.