regularConn = $this->em->getConnection(); parent::__construct($locker, $processRunner, $phpFinder); } protected function configure(): void { $this ->setName(self::NAME) ->setHidden() ->setDescription( 'Creates the database needed for shlink to work. It will do nothing if the database already exists', ); } protected function lockedExecute(InputInterface $input, OutputInterface $output): int { $io = new SymfonyStyle($input, $output); if ($this->databaseTablesExist()) { $io->success('Database already exists. Run "db:migrate" command to make sure it is up to date.'); return ExitCode::EXIT_SUCCESS; } // Create database $io->writeln('Creating database tables...'); $this->runPhpCommand($output, [self::DOCTRINE_SCRIPT, self::DOCTRINE_CREATE_SCHEMA_COMMAND]); $io->success('Database properly created!'); return ExitCode::EXIT_SUCCESS; } private function databaseTablesExist(): bool { $existingTables = $this->ensureDatabaseExistsAndGetTables(); $allMetadata = $this->em->getMetadataFactory()->getAllMetadata(); $shlinkTables = map($allMetadata, static fn (ClassMetadata $metadata) => $metadata->getTableName()); // 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($existingTables, $shlinkTable)); } 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 []; } } }