'migrations:generate', self::COMMAND_STATUS => 'migrations:status', self::COMMAND_MIGRATE => 'migrations:migrate', self::COMMAND_EXECUTE => 'migrations:execute', self::COMMAND_ROLLBACK => 'migrations:execute', self::COMMAND_ADD => 'migrations:version', self::COMMAND_INIT => 'migrations:sync-metadata-storage', ]; protected function provideOptions() { return [ 'command' => [ 'prefix' => 'c', 'longPrefix' => 'command', 'required' => true, 'description' => 'Command to be run' ], 'extension' => [ 'prefix' => 'e', 'longPrefix' => 'extension', 'required' => false, 'description' => 'Extension for which migration needs to be generated' ], 'version' => [ 'prefix' => 'v', 'longPrefix' => 'version', 'required' => false, 'description' => 'Version number to migrate' ], ]; } /** * @return string */ protected function provideDescription() { return 'Tao migrations tool'; } /** * @return Report * @throws ScriptException * @throws \common_ext_ExtensionException */ public function run() { $command = $this->getOption('command'); if (!isset($this->commands[$command])) { throw new ScriptException(sprintf('Command "%s" is not supported', $command)); } $output = $this->{$command}(); return new Report(Report::TYPE_INFO, $output->fetch()); } /** * @return BufferedOutput * @throws ScriptException * @throws \common_ext_ExtensionException */ private function generate() { $extension = $this->getExtension(); if (!is_dir($extension->getDir().self::MIGRATIONS_DIR)) { mkdir($extension->getDir().self::MIGRATIONS_DIR); } $input = [ 'command' => $this->commands[self::COMMAND_GENERATE], '--namespace' => $this->getExtensionNamespace($extension) ]; $configuration = $this->getConfiguration(); $taoRoot = $this->getServiceLocator()->get(ExtensionsManager::SERVICE_ID)->getExtensionById('tao')->getDir(); $configuration->setCustomTemplate( $taoRoot.'scripts'.DIRECTORY_SEPARATOR.'tools'.DIRECTORY_SEPARATOR.'migrations'.DIRECTORY_SEPARATOR.'Template.tpl' ); $dependencyFactory = $this->getDependencyFactory($configuration); $this->executeMigration($dependencyFactory, new ArrayInput($input), $output = new BufferedOutput()); return $output; } /** * @return BufferedOutput * @throws ScriptException * @throws \common_ext_ExtensionException */ private function migrate() { $input = ['command' => $this->commands[self::COMMAND_MIGRATE], '--no-interaction' => true]; if ($this->hasOption('version')) { $input['version'] = $this->getOption('version'); } $dependencyFactory = $this->getDependencyFactory($this->getConfiguration()); $this->executeMigration($dependencyFactory, new ArrayInput($input), $output = new BufferedOutput()); return $output; } /** * Add versions directly to the migrations table without executing them (skip migration) * @return BufferedOutput * @throws ScriptException * @throws \common_ext_ExtensionException */ private function add() { $input = ['command' => $this->commands[self::COMMAND_ADD], '--add' => true, '--all' => true, '--no-interaction' => true]; $this->executeMigration($this->getDependencyFactory($this->getConfiguration()), new ArrayInput($input), $output = new BufferedOutput()); return $output; } /** * @return BufferedOutput * @throws ScriptException * @throws \common_ext_ExtensionException */ private function init() { $input = ['command' => $this->commands[self::COMMAND_INIT], '--no-interaction' => true]; $this->executeMigration($this->getDependencyFactory($this->getConfiguration()), new ArrayInput($input), $output = new BufferedOutput()); return $output; } /** * @return BufferedOutput * @throws MigrationException * @throws ScriptException * @throws \common_ext_ExtensionException */ private function status() { $dependencyFactory = $this->getDependencyFactory($this->getConfiguration()); $this->executeMigration($dependencyFactory, new ArrayInput(['command' => $this->commands[self::COMMAND_STATUS]]), $output = new BufferedOutput()); return $output; } /** * @param bool $rollback * @return BufferedOutput * @throws MigrationException * @throws ScriptException * @throws \common_ext_ExtensionException */ private function execute(bool $rollback = false) { $input = [ 'command' => $this->commands[self::COMMAND_EXECUTE], 'versions' => [$this->getOption('version')], '--no-interaction' => true ]; if ($rollback) { $input['--down'] = true; } else { $input['--up'] = true; } $dependencyFactory = $this->getDependencyFactory($this->getConfiguration()); $this->executeMigration($dependencyFactory, new ArrayInput($input), $output = new BufferedOutput()); return $output; } /** * @return BufferedOutput * @throws MigrationException * @throws ScriptException * @throws \common_ext_ExtensionException */ private function rollback() { return $this->execute(true); } /** * @param DependencyFactory $dependencyFactory * @param InputInterface $input * @param OutputInterface|null $output * @throws ScriptException */ private function executeMigration(DependencyFactory $dependencyFactory, InputInterface $input, OutputInterface $output = null) { $cli = new Application('Doctrine Migrations'); $cli->setCatchExceptions(true); $cli->setAutoExit(false); $cli->addCommands(array( new GenerateCommand($dependencyFactory), new MigrateCommand($dependencyFactory), new StatusCommand($dependencyFactory), new ExecuteCommand($dependencyFactory), new VersionCommand($dependencyFactory), new SyncMetadataCommand($dependencyFactory), )); try { $cli->run($input, $output); } catch (NoMigrationsToExecute $e) { $output->write($e->getMessage()); } catch (\Exception $e) { $this->logWarning('Migration error: ' . $e->getMessage()); throw new ScriptException('Migration error: ' . $e->getMessage()); } } /** * @param Configuration $configuration * @return DependencyFactory * @throws ScriptException * @throws InvalidServiceManagerException */ private function getDependencyFactory($configuration) { $connection = $this->getConnection(); $dependencyFactory = DependencyFactory::fromConnection( new ExistingConfiguration($configuration), new ExistingConnection($connection) ); /** @var ExtensionsManager $extManager */ $extManager = $this->getServiceManager()->get(ExtensionsManager::SERVICE_ID); if ($this->hasOption('extension')) { $dependencyFactory->setService(ClassNameGenerator::class, new TaoClassNameGenerator($this->getExtension())); } $dependencyFactory->setService(Comparator::class, new TaoComparator($extManager, new \helpers_ExtensionHelper())); return $dependencyFactory; } /** * @return Configuration * @throws ScriptException * @throws \common_ext_ExtensionException */ private function getConfiguration() { $configuration = new Configuration(); /** @var ExtensionsManager $extensionManager */ $extensionManager = $this->getServiceLocator()->get(ExtensionsManager::SERVICE_ID); /** @var Extension $extension */ if ($this->hasOption('extension')) { $extensions = [$this->getExtension()]; } else { $extensions = $extensionManager->getInstalledExtensions(); } foreach ($extensions as $extension) { if (is_dir($extension->getDir().self::MIGRATIONS_DIR)) { $configuration->addMigrationsDirectory( $this->getExtensionNamespace($extension), $extension->getDir().self::MIGRATIONS_DIR ); } } return $configuration; } /** * @return Connection */ private function getConnection() { /** @var MigrationsService $migrationService */ $migrationService = $this->getServiceLocator()->get(MigrationsService::SERVICE_ID); return $migrationService->getPersistence()->getDriver()->getDbalConnection(); } /** * @return common_ext_Extension * @throws ScriptException */ private function getExtension() { /** @var ExtensionsManager $extensionManager */ $extensionManager = $this->getServiceLocator()->get(ExtensionsManager::SERVICE_ID); if (!$this->hasOption('extension')) { throw new ScriptException('Extension parameter missed'); } $extensionId = $this->getOption('extension'); if (!$extensionManager->isInstalled($extensionId)) { throw new ScriptException(sprintf('Extension "%s" is not installed', $extensionId)); } try { return $extensionManager->getExtensionById($extensionId); } catch (\common_ext_ExtensionException $e) { $this->logWarning('Error during extension retrieval: '.$e->getMessage()); throw new ScriptException(sprintf('Cannot retrieve extension "%s"', $extensionId)); } } /** * This is an assumption * @param common_ext_Extension $extension * @return string */ private function getExtensionNamespace(common_ext_Extension $extension) { return 'oat\\'.$extension->getId().'\\migrations'; } }