/**
  * {@inheritdoc}
  */
 protected function execute(InputInterface $input, OutputInterface $output)
 {
     $em = $this->getHelper('em')->getEntityManager();
     $validator = new SchemaValidator($em);
     $exit = 0;
     if ($input->getOption('skip-mapping')) {
         $output->writeln('<comment>[Mapping]  Skipped mapping check.</comment>');
     } elseif ($errors = $validator->validateMapping()) {
         foreach ($errors as $className => $errorMessages) {
             $output->writeln("<error>[Mapping]  FAIL - The entity-class '" . $className . "' mapping is invalid:</error>");
             foreach ($errorMessages as $errorMessage) {
                 $output->writeln('* ' . $errorMessage);
             }
             $output->writeln('');
         }
         $exit += 1;
     } else {
         $output->writeln('<info>[Mapping]  OK - The mapping files are correct.</info>');
     }
     if ($input->getOption('skip-sync')) {
         $output->writeln('<comment>[Database] SKIPPED - The database was not checked for synchronicity.</comment>');
     } elseif (!$validator->schemaInSyncWithMetadata()) {
         $output->writeln('<error>[Database] FAIL - The database schema is not in sync with the current mapping file.</error>');
         $exit += 2;
     } else {
         $output->writeln('<info>[Database] OK - The database schema is in sync with the mapping files.</info>');
     }
     return $exit;
 }
 public function fire(\Doctrine\ORM\EntityManagerInterface $d2em)
 {
     $validator = new SchemaValidator($d2em);
     $exit = 0;
     if ($this->option('skip-mapping')) {
         $this->comment('[Mapping]  Skipped mapping check.');
     } elseif ($errors = $validator->validateMapping()) {
         foreach ($errors as $className => $errorMessages) {
             $this->error("[Mapping]  FAIL - The entity-class '" . $className . "' mapping is invalid:");
             foreach ($errorMessages as $errorMessage) {
                 $this->line('* ' . $errorMessage);
             }
             $this->line();
         }
         $exit += 1;
     } else {
         $this->info("[Mapping]  OK - The mapping files are correct.");
     }
     if ($this->option('skip-sync')) {
         $this->comment("[Database] SKIPPED - The database was not checked for synchronicity.");
     } elseif (!$validator->schemaInSyncWithMetadata()) {
         $this->error("[Database] FAIL - The database schema is not in sync with the current mapping file.");
         $exit += 2;
     } else {
         $this->info("[Database] OK - The database schema is in sync with the mapping files.");
     }
 }
 /**
  * Execute the console command.
  *
  * @param ManagerRegistry $registry
  */
 public function fire(ManagerRegistry $registry)
 {
     $names = $this->option('em') ? [$this->option('em')] : $registry->getManagerNames();
     foreach ($names as $name) {
         $em = $registry->getManager($name);
         $validator = new SchemaValidator($em);
         $this->comment('');
         $this->message('Validating for <info>' . $name . '</info> entity manager...');
         if ($this->option('skip-mapping')) {
             $this->comment('Mapping]  Skipped mapping check.');
         } elseif ($errors = $validator->validateMapping()) {
             foreach ($errors as $className => $errorMessages) {
                 $this->error("[Mapping]  FAIL - The entity-class '" . $className . "' mapping is invalid:");
                 $this->comment('');
                 foreach ($errorMessages as $errorMessage) {
                     $this->message('* ' . $errorMessage, 'red');
                 }
             }
         } else {
             $this->info('[Mapping]  OK - The mapping files are correct.');
         }
         if ($this->option('skip-sync')) {
             $this->comment('Database] SKIPPED - The database was not checked for synchronicity.');
         } elseif (!$validator->schemaInSyncWithMetadata()) {
             $this->error('[Database] FAIL - The database schema is not in sync with the current mapping file.');
         } else {
             $this->info('[Database] OK - The database schema is in sync with the mapping files.');
         }
     }
 }
 /**
  * @Then I the tool should apply the DDL to the database
  */
 public function checkAppliedDDL()
 {
     $tool = new SchemaValidator($this->getEntityManager());
     Assertion::true($tool->schemaInSyncWithMetadata());
     Assertion::regex($this->tester->getDisplay(), '/Validated 1 manager, 1 needed schema appliance, 0 was in sync\\./');
 }
示例#5
0
 /**
  * checks if the schema in configured datastore is in sync with doctrine entities
  * @return boolean	true, if the schema is correct
  */
 public function checkSchema()
 {
     //get instances
     $ormController = OrmController::create();
     $entityManager = $ormController->getEntityManager();
     $schemaValidator = new SchemaValidator($entityManager);
     //check schema
     return $schemaValidator->schemaInSyncWithMetadata();
 }
 /**
  * {@inheritdoc}
  *
  * @throws \InvalidArgumentException If the strategy is invalid.
  * @throws \InvalidArgumentException If the fixtures config is invalid.
  */
 protected function execute(InputInterface $input, OutputInterface $output) : int
 {
     $environment = $this->getContainer()->getParameter('kernel.environment');
     $application = $this->getApplication();
     $strategy = $input->getOption('strategy');
     $productionFixtures = $input->getOption('production-fixtures');
     $validatedDatabases = [];
     if (!in_array($strategy, [self::STRATEGY_SCHEMA_UPDATE, self::STRATEGY_MIGRATIONS], true)) {
         throw new \InvalidArgumentException(sprintf('The strategy must be either "%s" or "%s"!', self::STRATEGY_MIGRATIONS, self::STRATEGY_SCHEMA_UPDATE));
     }
     /** @var \Doctrine\Common\Persistence\ManagerRegistry $registry */
     $registry = $this->getContainer()->get('doctrine');
     // initialize counters
     $count = $applied = $fixtureApplied = $inSync = 0;
     foreach ($input->getArgument('managers') as $manager) {
         $output->writeln(sprintf('<comment>Applying schema for manager "%s"...</comment>', $manager));
         ++$count;
         /** @var \Doctrine\ORM\EntityManagerInterface $em */
         $em = $registry->getManager($manager);
         $tool = new SchemaTool($em);
         $validator = new SchemaValidator($em);
         // the SchemaValidator is able to test the internal entity metadata
         // against the database DDL. If multiple entity managers have the same connection,
         // every validator instance will ONLY check the metadata of its associated
         // entity manager.
         if (!$validator->schemaInSyncWithMetadata()) {
             // execute strategy by schema
             if (self::STRATEGY_SCHEMA_UPDATE === $strategy) {
                 // At this place we UPDATE the whole schema instead of applying everything:
                 // This is due to the issue that a schema can be partially in sync, but this tool would detect
                 // the schema of the corresponding entity manager as outdated and would run the appliance again.
                 // If the appliance would force to recreate everything, tons of `Table or view exists` errors
                 // would arise.
                 $tool->updateSchema($em->getMetadataFactory()->getAllMetadata());
             } else {
                 // Especially on prod environments it's safer to rely on the migrations framework.
                 $command = $application->find('doctrine:migrations:migrate');
                 $migrationInput = new ArrayInput(['--env' => $environment]);
                 // migrations should be used in order to ensure on production environments
                 // with already existing data that the data will be preserved
                 // using custom migration classes for DDL changes.
                 $migrationInput->setInteractive(false);
                 $command->run($migrationInput, new NullOutput());
             }
             ++$applied;
         } else {
             ++$inSync;
         }
         $shouldApplyFixtures = $input->getOption('apply-fixtures');
         if ($productionFixtures && !$shouldApplyFixtures) {
             throw new \InvalidArgumentException('The `--production-fixtures` option must not be set if the `--apply-fixtures` option is not present!');
         }
         // fixture appliance:
         // the database installation process may include a fixture appliance, if the `--apply-fixtures` flag
         // is set.
         // If configured, only production fixtures will be applied.
         if ($shouldApplyFixtures) {
             $connection = $em->getConnection();
             $database = $connection->getDatabase();
             $shouldAppend = $apply = $input->getOption('append');
             // ensure that the validation is not executed multiple times.
             // if two entity managers have the same connection, but different metadata
             // and the database is not empty, this information should be cached in order
             // to avoid calculations when possible.
             if (!isset($validatedDatabases[$database]) && !$shouldAppend) {
                 // build an SQL query which ensures that the database is empty
                 $migrationsTable = $this->getContainer()->getParameter('doctrine_migrations.table_name');
                 $template = '(SELECT COUNT(:col) FROM :table)';
                 $tables = $connection->getSchemaManager()->createSchema()->getTables();
                 $tableCountSQL = array_map(function (Table $table) use($template, $migrationsTable) : string {
                     $name = $table->getName();
                     // migration table needs to be skipped.
                     if ($migrationsTable === $name) {
                         return '(0)';
                     }
                     $primaryKeys = $table->getPrimaryKeyColumns();
                     $column = count($primaryKeys) === 0 ? '*' : $primaryKeys[0];
                     return strtr($template, [':col' => $column, ':table' => $name]);
                 }, $tables);
                 $statement = $connection->prepare(sprintf('SELECT (%s) AS rows', implode('+', $tableCountSQL)));
                 $statement->execute();
                 // if the result is higher than zero, the appliance should be skipped
                 // since it the table is not empty.
                 $validatedDatabases[$database] = $apply = (int) $statement->fetch()['rows'] === 0;
             } elseif (isset($validatedDatabases[$database])) {
                 $apply = $validatedDatabases[$database];
             }
             if ($input->isInteractive() && !$apply) {
                 $question = new ConfirmationQuestion('<question>Careful, database is not empty. Should the fixtures be applied anyway (y/n)?</question>', false);
                 /** @var \Symfony\Component\Console\Helper\QuestionHelper $questionHelper */
                 $questionHelper = $this->getHelper('question');
                 if (!$questionHelper->ask($input, $output, $question)) {
                     $output->writeln(sprintf('<comment>Skipping fixture appliance for manager "%s"!</comment>', $manager));
                     continue;
                 }
                 $apply = true;
             }
             // checks whether fixtures should be applied (`--apply-fixtures`) and whether the database
             // is empty. Unless the whole process will be interrupted to avoid clearing accidentally the database.
             if ($apply) {
                 $target = $productionFixtures ? 'sententiaregum:fixtures:production' : 'doctrine:fixtures:load';
                 $output->writeln(sprintf('<comment>Applying fixtures for manager "%s" with environment "%s"!</comment>', $manager, $environment));
                 $args = ['--env' => $environment];
                 if ($shouldAppend) {
                     // the `LoadCustomFixturesCommand` doesn't support appending due to the fact that
                     // the production fixtures are only some very basic items that don't need to be appended,
                     // but should be executed at first and changed by migrations (model partially depends).
                     if ($productionFixtures) {
                         $output->writeln(sprintf('<error>Skipping `--append` flag for manager "%s" because `--production-fixtures` option is set</error>', $manager));
                         continue;
                     } else {
                         $args['--append'] = true;
                     }
                 }
                 $command = $application->find($target);
                 $fixtureInput = new ArrayInput($args);
                 $fixtureInput->setInteractive(false);
                 $command->run($fixtureInput, new NullOutput());
                 ++$fixtureApplied;
                 continue;
             }
         }
         // the skip notice should be displayed all the time if the fixture flag is set,
         // but the notice will be skipped.
         $message = sprintf('Skipping fixture appliance for manager "%s" because the database is not empty %s', $manager, $input->isInteractive() ? ' and the confirmation has been answered with no' : ' and the interaction mode is disabled, so no confirmation whether to force appliance can be used');
         $output->writeln(sprintf('<comment>%s!</comment>', $message));
     }
     // add an empty line before result stats
     $output->writeln('');
     $output->writeln(sprintf('<info>Validated %d %s, %d needed schema appliance, %d %s in sync. %d %s %s fixtures were applied.</info>', $count, $this->pluralize($count, 'managers', 'manager'), $applied, $inSync, $this->pluralize($count, 'were', 'was'), $fixtureApplied, $this->pluralize($fixtureApplied, 'times', 'time'), $productionFixtures ? 'production' : 'all'));
     return 0;
 }