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); } }