Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Migration commands refactoring #83

Merged
merged 3 commits into from
Nov 21, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@
"infection/infection": "^0.26.6",
"mockery/mockery": "^1.5",
"phpunit/phpunit": "^9.5.20",
"spiral/testing": "^2.0",
"spiral/testing": "^2.4",
"spiral/validator": "^1.2",
"vimeo/psalm": "^4.27"
},
Expand Down
2 changes: 2 additions & 0 deletions src/Auth/TokenStorage.php
Original file line number Diff line number Diff line change
Expand Up @@ -89,11 +89,13 @@ private function issueID(): string
{
$id = $this->randomHash(64);

/** @psalm-suppress InternalMethod */
$query = $this->orm->getSource(Token::class)
->getDatabase()
->select()
->from($this->orm->getSource(Token::class)->getTable());

/** @psalm-suppress InternalMethod */
while ((clone $query)->where('id', $id)->count('id') !== 0) {
$id = $this->randomHash(64);
}
Expand Down
23 changes: 12 additions & 11 deletions src/Console/Command/CycleOrm/Generator/ShowChanges.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,31 +4,29 @@

namespace Spiral\Cycle\Console\Command\CycleOrm\Generator;

use Cycle\Database\Schema\ComparatorInterface;
use Cycle\Schema\GeneratorInterface;
use Cycle\Schema\Registry;
use Cycle\Database\Schema\AbstractTable;
use Cycle\Database\Schema\Comparator;
use Symfony\Component\Console\Output\OutputInterface;

final class ShowChanges implements GeneratorInterface
{
private array $changes = [];

public function __construct(
private readonly OutputInterface $output
private readonly OutputInterface $output,
) {
}

public function run(Registry $registry): Registry
{
$this->output->writeln('<info>Detecting schema changes:</info>');

$this->changes = [];
foreach ($registry->getIterator() as $e) {
if ($registry->hasTable($e)) {
$table = $registry->getTableSchema($e);
if ($table->getComparator()->hasChanges()) {
$key = $registry->getDatabase($e).':'.$registry->getTable($e);
$key = $registry->getDatabase($e) . ':' . $registry->getTable($e);
$this->changes[$key] = [
'database' => $registry->getDatabase($e),
'table' => $registry->getTable($e),
Expand All @@ -44,6 +42,9 @@ public function run(Registry $registry): Registry
return $registry;
}


$this->output->writeln('<info>Schema changes:</info>');

foreach ($this->changes as $change) {
$this->output->write(\sprintf('• <fg=cyan>%s.%s</fg=cyan>', $change['database'], $change['table']));
$this->describeChanges($change['schema']);
Expand All @@ -63,14 +64,14 @@ protected function describeChanges(AbstractTable $table): void
$this->output->writeln(
\sprintf(
': <fg=green>%s</fg=green> change(s) detected',
$this->numChanges($table)
)
$this->numChanges($table),
),
);

return;
}
$this->output->write("\n");

$this->output->write("\n");

if (!$table->exists()) {
$this->output->writeln(' - create table');
Expand All @@ -88,7 +89,7 @@ protected function describeChanges(AbstractTable $table): void
$this->describeFKs($cmp);
}

protected function describeColumns(Comparator $cmp): void
protected function describeColumns(ComparatorInterface $cmp): void
{
foreach ($cmp->addedColumns() as $column) {
$this->output->writeln(" - add column <fg=yellow>{$column->getName()}</fg=yellow>");
Expand All @@ -104,7 +105,7 @@ protected function describeColumns(Comparator $cmp): void
}
}

protected function describeIndexes(Comparator $cmp): void
protected function describeIndexes(ComparatorInterface $cmp): void
{
foreach ($cmp->addedIndexes() as $index) {
$index = \implode(', ', $index->getColumns());
Expand All @@ -123,7 +124,7 @@ protected function describeIndexes(Comparator $cmp): void
}
}

protected function describeFKs(Comparator $cmp): void
protected function describeFKs(ComparatorInterface $cmp): void
{
foreach ($cmp->addedForeignKeys() as $fk) {
$fkColumns = \implode(', ', $fk->getColumns());
Expand Down
30 changes: 24 additions & 6 deletions src/Console/Command/CycleOrm/MigrateCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@

namespace Spiral\Cycle\Console\Command\CycleOrm;

use Cycle\Schema\Generator\Migrations\Strategy\GeneratorStrategyInterface;
use Cycle\Schema\Generator\Migrations\Strategy\MultipleFilesStrategy;
use Spiral\Core\BinderInterface;
use Spiral\Cycle\Bootloader\SchemaBootloader;
use Spiral\Cycle\Config\CycleConfig;
use Spiral\Cycle\Console\Command\CycleOrm\Generator\ShowChanges;
Expand All @@ -22,6 +25,7 @@ final class MigrateCommand extends AbstractCommand
protected const NAME = 'cycle:migrate';
protected const DESCRIPTION = 'Generate ORM schema migrations';
protected const OPTIONS = [
['split', 'p', InputOption::VALUE_NONE, 'Split generated migration into multiple files.'],
['run', 'r', InputOption::VALUE_NONE, 'Automatically run generated migration.'],
];

Expand All @@ -30,30 +34,44 @@ public function perform(
CycleConfig $config,
Registry $registry,
MemoryInterface $memory,
GenerateMigrations $migrations,
Migrator $migrator,
Console $console
Console $console,
): int {
$migrator->configure();

foreach ($migrator->getMigrations() as $migration) {
if ($migration->getState()->getStatus() !== State::STATUS_EXECUTED) {
$this->writeln('<fg=red>Outstanding migrations found, run `migrate` first.</fg=red>');
return self::SUCCESS;
$this->error('Outstanding migrations found.');

if ($this->isInteractive() && $this->output->confirm('Do you want to run `migrate` now?')) {
$console->run('migrate', [], $this->output);
} else {
$this->error('Please run `migrate` first.');
return self::SUCCESS;
}
}
}

$this->comment('Detecting schema changes...');

$schemaCompiler = Compiler::compile(
$registry,
\array_merge($bootloader->getGenerators($config), [
$show = new ShowChanges($this->output)
$show = new ShowChanges($this->output),
]),
$config->getSchemaDefaults()
$config->getSchemaDefaults(),
);

$schemaCompiler->toMemory($memory);

if ($show->hasChanges()) {
if ($this->option('split')) {
\assert($this->container instanceof BinderInterface);
$this->container->bind(GeneratorStrategyInterface::class, MultipleFilesStrategy::class);
}

$migrations = $this->container->get(GenerateMigrations::class);

(new \Cycle\Schema\Compiler())->compile($registry, [$migrations]);

if ($this->option('run')) {
Expand Down
1 change: 0 additions & 1 deletion src/Console/Command/CycleOrm/RenderCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@
final class RenderCommand extends AbstractCommand
{
protected const SIGNATURE = 'cycle:render {format=color : Output format}';

protected const DESCRIPTION = 'Render available CycleORM schemas';

public function perform(
Expand Down
15 changes: 10 additions & 5 deletions src/Console/Command/CycleOrm/SyncCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,12 @@
use Cycle\Schema\Generator\SyncTables;
use Cycle\Schema\Registry;
use Spiral\Boot\MemoryInterface;
use Spiral\Console\Command;
use Spiral\Cycle\Config\CycleConfig;
use Spiral\Cycle\Console\Command\CycleOrm\Generator\ShowChanges;
use Spiral\Cycle\Console\Command\Migrate\AbstractCommand;
use Spiral\Cycle\Schema\Compiler;

final class SyncCommand extends Command
final class SyncCommand extends AbstractCommand
{
protected const NAME = 'cycle:sync';
protected const DESCRIPTION = 'Sync Cycle ORM schema with database without intermediate migration (risk operation)';
Expand All @@ -22,19 +22,24 @@ public function perform(
SchemaBootloader $bootloader,
CycleConfig $config,
Registry $registry,
MemoryInterface $memory
MemoryInterface $memory,
): int {
if (!$this->verifyEnvironment(message: 'This operation is not recommended for production environment.')) {
return self::FAILURE;
}

$show = new ShowChanges($this->output);

$schemaCompiler = Compiler::compile(
$registry,
\array_merge($bootloader->getGenerators($config), [$show, new SyncTables()]),
$config->getSchemaDefaults()
$config->getSchemaDefaults(),
);

$schemaCompiler->toMemory($memory);

if ($show->hasChanges()) {
$this->writeln("\n<info>ORM Schema has been synchronized</info>");
$this->info('ORM Schema has been synchronized with database.');
}

return self::SUCCESS;
Expand Down
8 changes: 4 additions & 4 deletions src/Console/Command/CycleOrm/UpdateCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,17 +20,17 @@ public function perform(
SchemaBootloader $bootloader,
CycleConfig $config,
Registry $registry,
MemoryInterface $memory
MemoryInterface $memory,
): int {
$this->write('Updating ORM schema... ');
$this->info('Updating ORM schema... ');

Compiler::compile(
$registry,
$bootloader->getGenerators($config),
$config->getSchemaDefaults()
$config->getSchemaDefaults(),
)->toMemory($memory);

$this->writeln('<info>done</info>');
$this->info('Schema has been updated.');

return self::SUCCESS;
}
Expand Down
27 changes: 19 additions & 8 deletions src/Console/Command/Migrate/AbstractCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ abstract class AbstractCommand extends Command
{
public function __construct(
protected Migrator $migrator,
protected MigrationConfig $config
protected MigrationConfig $config,
) {
parent::__construct();
}
Expand All @@ -24,7 +24,7 @@ protected function verifyConfigured(): bool
{
if (!$this->migrator->isConfigured()) {
$this->writeln(
"<fg=red>Migrations are not configured yet, run '<info>migrate:init</info>' first.</fg=red>"
"<fg=red>Migrations are not configured yet, run '<info>migrate:init</info>' first.</fg=red>",
);

return false;
Expand All @@ -36,17 +36,17 @@ protected function verifyConfigured(): bool
/**
* Check if current environment is safe to run migration.
*/
protected function verifyEnvironment(): bool
protected function verifyEnvironment(string $message = 'Confirmation is required to run migrations!'): bool
{
if ($this->option('force') || $this->config->isSafe()) {
if ($this->isForce() || $this->config->isSafe()) {
//Safe to run
return true;
}

$this->writeln('<fg=red>Confirmation is required to run migrations!</fg=red>');
$this->error($message);

if (!$this->askConfirmation()) {
$this->writeln('<comment>Cancelling operation...</comment>');
$this->comment('Cancelling operation...');

return false;
}
Expand All @@ -60,7 +60,8 @@ protected function defineOptions(): array
static::OPTIONS,
[
['force', 's', InputOption::VALUE_NONE, 'Skip safe environment check'],
]
['no-interaction', 'n', InputOption::VALUE_NONE, 'Do not ask any interactive question'],
],
);
}

Expand All @@ -71,7 +72,17 @@ protected function askConfirmation(): bool
return $question->ask(
$this->input,
$this->output,
new ConfirmationQuestion('<question>Would you like to continue?</question> ')
new ConfirmationQuestion('<question>Would you like to continue?</question> ', false),
);
}

protected function isInteractive(): bool
{
return !$this->option('no-interaction');
}

protected function isForce(): bool
{
return $this->option('force');
}
}
4 changes: 2 additions & 2 deletions src/Console/Command/Migrate/InitCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,15 @@
final class InitCommand extends AbstractCommand
{
protected const NAME = 'migrate:init';
protected const DESCRIPTION = 'Init migrations component (create migrations table)';
protected const DESCRIPTION = 'Create migrations table if not exists.';

/**
* Perform command.
*/
public function perform(): int
{
$this->migrator->configure();
$this->writeln('<info>Migrations table were successfully created</info>');
$this->info('Migration table was successfully created.');

return self::SUCCESS;
}
Expand Down
6 changes: 3 additions & 3 deletions src/Console/Command/Migrate/MigrateCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
final class MigrateCommand extends AbstractCommand
{
protected const NAME = 'migrate';
protected const DESCRIPTION = 'Perform one or all outstanding migrations';
protected const DESCRIPTION = 'Execute one or multiple migrations.';
protected const OPTIONS = [
['one', 'o', InputOption::VALUE_NONE, 'Execute only one (first) migration'],
];
Expand All @@ -34,12 +34,12 @@ public function perform(): int

$this->sprintf(
"<info>Migration <comment>%s</comment> was successfully executed.</info>\n",
$migration->getState()->getName()
$migration->getState()->getName(),
);
}

if (!$found) {
$this->writeln('<fg=red>No outstanding migrations were found.</fg=red>');
$this->error('No outstanding migrations were found.');
}

return self::SUCCESS;
Expand Down
5 changes: 2 additions & 3 deletions src/Console/Command/Migrate/ReplayCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ final class ReplayCommand extends AbstractCommand
];

/**
* @param Console $console
* @throws \Throwable
*/
public function perform(Console $console): int
Expand All @@ -35,12 +34,12 @@ public function perform(Console $console): int
$migrate['--one'] = true;
}

$this->writeln('Rolling back executed migration(s)...');
$this->warning('Rolling back executed migration(s)...');
$console->run('migrate:rollback', $rollback, $this->output);

$this->writeln('');

$this->writeln('Executing outstanding migration(s)...');
$this->info('Executing outstanding migration(s)...');
$console->run('migrate', $migrate, $this->output);

return self::SUCCESS;
Expand Down
Loading
Loading