0

Final Class Yiisoft\Db\Migration\Command\RedoCommand

InheritanceYiisoft\Db\Migration\Command\RedoCommand » Yiisoft\Db\Migration\Command\DatabaseCommand » Symfony\Component\Console\Command\Command

Redoes the last few migrations.

This command will first revert the specified migrations, and then apply them again.

For example:

./yii migrate:redo                                           # redo the last applied migration
./yii migrate:redo --limit=3                                 # redo last 3 applied migrations
./yii migrate:redo --all                                     # redo all migrations
./yii migrate:redo --path=@vendor/yiisoft/rbac-db/migrations # redo the last migration from the directory
./yii migrate:redo --namespace=Yiisoft\\Rbac\\Db\\Migrations # redo the last migration from the namespace

Method Details

Hide inherited methods

__construct() public method

public mixed __construct ( Yiisoft\Db\Migration\Service\MigrationService $migrationService, Yiisoft\Db\Migration\Migrator $migrator, Yiisoft\Db\Migration\Runner\DownRunner $downRunner, Yiisoft\Db\Migration\Runner\UpdateRunner $updateRunner, ?\Yiisoft\Db\Migration\DatabaseRegistry $additionalDatabases = null )
$migrationService Yiisoft\Db\Migration\Service\MigrationService
$migrator Yiisoft\Db\Migration\Migrator
$downRunner Yiisoft\Db\Migration\Runner\DownRunner
$updateRunner Yiisoft\Db\Migration\Runner\UpdateRunner
$additionalDatabases ?\Yiisoft\Db\Migration\DatabaseRegistry

                public function __construct(
    private readonly MigrationService $migrationService,
    private readonly Migrator $migrator,
    private readonly DownRunner $downRunner,
    private readonly UpdateRunner $updateRunner,
    ?DatabaseRegistry $additionalDatabases = null,
) {
    parent::__construct($additionalDatabases);
}

            
configure() protected method

protected void configure ( )

                protected function configure(): void
{
    $this
        ->addOption('limit', 'l', InputOption::VALUE_REQUIRED, 'Number of migrations to redo.', 1)
        ->addOption('all', 'a', InputOption::VALUE_NONE, 'Redo all migrations.')
        ->addOption('path', null, InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY, 'Path to migrations to redo.')
        ->addOption('namespace', 'ns', InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY, 'Namespace of migrations to redo.')
        ->addOption('force-yes', 'y', InputOption::VALUE_NONE, 'Force yes to all questions.');
}

            
createContext() protected method

protected Yiisoft\Db\Migration\Command\Context\RedoContext createContext ( Yiisoft\Db\Migration\DatabaseSetup $setup )
$setup Yiisoft\Db\Migration\DatabaseSetup

                protected function createContext(DatabaseSetup $setup): RedoContext
{
    return new RedoContext(
        $setup->migrator,
        $setup->migrationService,
        new DownRunner($setup->migrator),
        new UpdateRunner($setup->migrator),
    );
}

            
createDefaultContext() protected method

protected Yiisoft\Db\Migration\Command\Context\RedoContext createDefaultContext ( )

                protected function createDefaultContext(): RedoContext
{
    return new RedoContext($this->migrator, $this->migrationService, $this->downRunner, $this->updateRunner);
}

            
execute() protected method
protected integer execute ( \Symfony\Component\Console\Input\InputInterface $input, \Symfony\Component\Console\Output\OutputInterface $output )
$input \Symfony\Component\Console\Input\InputInterface
$output \Symfony\Component\Console\Output\OutputInterface

                final protected function execute(InputInterface $input, OutputInterface $output): int
{
    $io = new SymfonyStyle($input, $output);
    $names = $this->resolveDatabaseNames($input);
    if (is_string($names)) {
        $io->error($names);
        return Command::INVALID;
    }
    $multipleDatabases = $this->isMultipleDatabases();
    $hasResults = false;
    foreach ($names as $name) {
        if (!$multipleDatabases || $name === DatabaseRegistry::DEFAULT_DATABASE) {
            $context = $this->createDefaultContext();
        } else {
            /** @psalm-var DatabaseRegistry $this->additionalDatabases */
            $context = $this->createContext($this->additionalDatabases->createSetup($name));
        }
        if ($multipleDatabases) {
            $io->section('Database: ' . OutputFormatter::escape($name));
        }
        $status = $this->executeForDatabase($input, $output, $context);
        if ($status === null) {
            continue;
        }
        if ($status !== Command::SUCCESS) {
            return $status;
        }
        $hasResults = true;
    }
    return $hasResults ? Command::SUCCESS : Command::FAILURE;
}

            
executeForDatabase() protected method

protected ?int executeForDatabase ( \Symfony\Component\Console\Input\InputInterface $input, \Symfony\Component\Console\Output\OutputInterface $output, object $context )
$input \Symfony\Component\Console\Input\InputInterface
$output \Symfony\Component\Console\Output\OutputInterface
$context object

                protected function executeForDatabase(
    InputInterface $input,
    OutputInterface $output,
    object $context,
): ?int {
    $io = new SymfonyStyle($input, $output);
    $context->migrator->setIo($io);
    $context->migrationService->setIo($io);
    $context->downRunner->setIo($io);
    $context->updateRunner->setIo($io);
    $context->migrationService->databaseConnection();
    $context->migrationService->before($this->getName() ?? '');
    $limit = !$input->getOption('all')
        ? (int) $input->getOption('limit')
        : null;
    if ($limit !== null && $limit <= 0) {
        $io->error('The limit option must be greater than 0.');
        return Command::INVALID;
    }
    /** @var string[] $paths */
    $paths = $input->getOption('path');
    /** @var string[] $namespaces */
    $namespaces = $input->getOption('namespace');
    if (!empty($paths) || !empty($namespaces)) {
        $migrations = $context->migrator->getHistory();
        $migrations = array_keys($migrations);
        $migrations = $context->migrationService->filterMigrations($migrations, $namespaces, $paths);
        if (empty($migrations)) {
            $io->warning('No applied migrations found.');
            return null;
        }
        if ($limit !== null) {
            $migrations = array_slice($migrations, 0, $limit);
        }
    } else {
        $migrations = $context->migrator->getHistory($limit);
        if (empty($migrations)) {
            $io->warning('No migration has been done before.');
            return null;
        }
        $migrations = array_keys($migrations);
    }
    $countMigrations = count($migrations);
    $migrationWord = $countMigrations === 1 ? 'migration' : 'migrations';
    $output->writeln("<warning>Total $countMigrations $migrationWord to be redone:</warning>\n");
    foreach ($migrations as $i => $migration) {
        $output->writeln("\t<info>" . ($i + 1) . ". $migration</info>");
    }
    if ($input->getOption('force-yes') || $io->confirm("Redo the above $migrationWord?")) {
        $instances = $context->migrationService->makeRevertibleMigrations($migrations);
        $migrationWas = ($countMigrations === 1 ? 'migration was' : 'migrations were');
        foreach ($instances as $i => $instance) {
            try {
                $context->downRunner->run($instance, $i + 1);
            } catch (Throwable $e) {
                $output->writeln("\n<fg=yellow>Total $i out of $countMigrations $migrationWas reverted.</>\n");
                $io->error($i > 0 ? 'Partially reverted.' : 'Not reverted.');
                throw $e;
            }
        }
        foreach (array_reverse($instances) as $i => $instance) {
            try {
                $context->updateRunner->run($instance, $countMigrations - $i);
            } catch (Throwable $e) {
                $output->writeln("\n<fg=yellow>Total $i out of $countMigrations $migrationWas applied.</>\n");
                $io->error($i > 0 ? 'Reverted but partially applied.' : 'Reverted but not applied.');
                throw $e;
            }
        }
        $output->writeln("\n<info>$countMigrations $migrationWas redone.</info>\n");
        $io->success('Migration redone successfully.');
    }
    return Command::SUCCESS;
}

            
getAvailableDatabaseNames() protected method
protected string[] getAvailableDatabaseNames ( )

                protected function getAvailableDatabaseNames(): array
{
    return [DatabaseRegistry::DEFAULT_DATABASE, ...($this->additionalDatabases?->getNames() ?? [])];
}

            
isMultipleDatabases() protected method
protected boolean isMultipleDatabases ( )

                protected function isMultipleDatabases(): bool
{
    return $this->additionalDatabases !== null && $this->additionalDatabases->getNames() !== [];
}

            
resolveDatabaseNamesWithMissingOption() protected method

Defined in: Yiisoft\Db\Migration\Command\DatabaseCommand::resolveDatabaseNamesWithMissingOption()

Resolves the database names or returns a message indicating that an option is missing if multiple databases are configured.

protected string[]|string resolveDatabaseNamesWithMissingOption ( \Symfony\Component\Console\Input\InputInterface $input )
$input \Symfony\Component\Console\Input\InputInterface

The input interface containing the command options and arguments.

                protected function resolveDatabaseNamesWithMissingOption(InputInterface $input): array|string
{
    if (!$this->isMultipleDatabases()) {
        return [DatabaseRegistry::DEFAULT_DATABASE];
    }
    return 'The --db option is required when multiple databases are configured.';
}