regularConn = $this->em->getConnection(); parent::__construct(); } public function __invoke(SymfonyStyle $io): int { return CommandUtils::executeWithLock( $this->locker, LockConfig::blocking(self::NAME), $io, fn () => $this->executeCommand($io), ); } private function executeCommand(SymfonyStyle $io): int { if ($this->databaseTablesExist()) { $io->success('Database already exists. Run "db:migrate" command to make sure it is up to date.'); return self::SUCCESS; } // Create database $io->writeln('Creating database tables...'); $this->processRunner->run($io, [self::SCRIPT, self::COMMAND, '--no-interaction']); $io->success('Database properly created!'); return self::SUCCESS; } private function databaseTablesExist(): bool { $existingTables = $this->ensureDatabaseExistsAndGetTables(); $allMetadata = $this->em->getMetadataFactory()->getAllMetadata(); $shlinkTables = array_map(static fn (ClassMetadata $metadata) => $metadata->getTableName(), $allMetadata); // If at least one of the shlink tables exist, we will consider the database exists somehow. // Any other inconsistency will be taken care of by the migrations. return some($shlinkTables, static fn (string $shlinkTable) => contains($shlinkTable, $existingTables)); } private function ensureDatabaseExistsAndGetTables(): array { try { // Trying to list tables requires opening a connection to configured database. // If it fails, it means it does not exist yet. return $this->regularConn->createSchemaManager()->listTableNames(); } catch (Throwable) { // We cannot use getDatabase() to get the database name here, because then the driver will try to connect. // Instead, we read from the raw params. $shlinkDatabase = $this->regularConn->getParams()['dbname'] ?? ''; // Create the database using a connection where the dbname was not set. $this->noDbNameConn->createSchemaManager()->createDatabase($shlinkDatabase); return []; } } }