setAliases(['diff'])
->setDescription('Generate a migration by comparing your current database to your mapping information.')
->setHelp(<<<'EOT'
The %command.name% command generates a migration by comparing your current database to your mapping information:
%command.full_name%
EOT)
->addOption(
'namespace',
null,
InputOption::VALUE_REQUIRED,
'The namespace to use for the migration (must be in the list of configured namespaces)',
)
->addOption(
'filter-expression',
null,
InputOption::VALUE_REQUIRED,
'Tables which are filtered by Regular Expression.',
)
->addOption(
'formatted',
null,
InputOption::VALUE_NONE,
'Format the generated SQL.',
)
->addOption(
'line-length',
null,
InputOption::VALUE_REQUIRED,
'Max line length of unformatted lines.',
'120',
)
->addOption(
'check-database-platform',
null,
InputOption::VALUE_OPTIONAL,
'Check Database Platform to the generated code.',
false,
)
->addOption(
'allow-empty-diff',
null,
InputOption::VALUE_NONE,
'Do not throw an exception when no changes are detected.',
)
->addOption(
'from-empty-schema',
null,
InputOption::VALUE_NONE,
'Generate a full migration as if the current database was empty.',
);
}
/** @throws InvalidOptionUsage */
protected function execute(
InputInterface $input,
OutputInterface $output,
): int {
$filterExpression = (string) $input->getOption('filter-expression');
if ($filterExpression === '') {
$filterExpression = null;
}
$formatted = filter_var($input->getOption('formatted'), FILTER_VALIDATE_BOOLEAN);
$lineLength = (int) $input->getOption('line-length');
$allowEmptyDiff = $input->getOption('allow-empty-diff');
$checkDbPlatform = filter_var($input->getOption('check-database-platform'), FILTER_VALIDATE_BOOLEAN);
$fromEmptySchema = $input->getOption('from-empty-schema');
if ($formatted) {
if (! class_exists(SqlFormatter::class)) {
throw InvalidOptionUsage::new(
'The "--formatted" option can only be used if the sql formatter is installed. Please run "composer require doctrine/sql-formatter".',
);
}
}
$namespace = $this->getNamespace($input, $output);
$statusCalculator = $this->getDependencyFactory()->getMigrationStatusCalculator();
$executedUnavailableMigrations = $statusCalculator->getExecutedUnavailableMigrations();
$newMigrations = $statusCalculator->getNewMigrations();
if (! $this->checkNewMigrationsOrExecutedUnavailable($newMigrations, $executedUnavailableMigrations, $input)) {
$this->io->error('Migration cancelled!');
return 3;
}
$fqcn = $this->getDependencyFactory()->getClassNameGenerator()->generateClassName($namespace);
$diffGenerator = $this->getDependencyFactory()->getDiffGenerator();
try {
$path = $diffGenerator->generate(
$fqcn,
$filterExpression,
$formatted,
$lineLength,
$checkDbPlatform,
$fromEmptySchema,
);
} catch (NoChangesDetected $exception) {
if ($allowEmptyDiff) {
$this->io->error($exception->getMessage());
return 0;
}
throw $exception;
}
$this->io->text([
sprintf('Generated new migration class to "%s"', $path),
'',
sprintf(
'To run just this migration for testing purposes, you can use migrations:execute --up "%s"',
addslashes($fqcn),
),
'',
sprintf(
'To revert the migration you can use migrations:execute --down "%s"',
addslashes($fqcn),
),
'',
]);
return 0;
}
private function checkNewMigrationsOrExecutedUnavailable(
AvailableMigrationsList $newMigrations,
ExecutedMigrationsList $executedUnavailableMigrations,
InputInterface $input,
): bool {
if (count($newMigrations) === 0 && count($executedUnavailableMigrations) === 0) {
return true;
}
if (count($newMigrations) !== 0) {
$this->io->warning(sprintf(
'You have %d available migrations to execute.',
count($newMigrations),
));
}
if (count($executedUnavailableMigrations) !== 0) {
$this->io->warning(sprintf(
'You have %d previously executed migrations in the database that are not registered migrations.',
count($executedUnavailableMigrations),
));
}
return $this->canExecute('Are you sure you wish to continue?', $input);
}
}