diff --git a/config/services.yaml b/config/services.yaml index 13744b48..b6acd224 100644 --- a/config/services.yaml +++ b/config/services.yaml @@ -21,6 +21,7 @@ services: $emailSender: '%app.notifications.email_sender%' $projectDir: '%kernel.project_dir%' $publicFolder: '%bolt.public_folder%' + $tablePrefix: '%bolt.table_prefix%' # makes classes in src/ available to be used as services # this creates a service per class whose id is the fully-qualified class name @@ -41,7 +42,6 @@ services: $debug: '%kernel.debug%' Bolt\Doctrine\TablePrefix: - arguments: [ "%bolt.table_prefix%" ] tags: - { name: doctrine.event_listener, event: loadClassMetadata, lazy: true } @@ -54,9 +54,6 @@ services: tags: - { name: doctrine.event_listener, event: loadClassMetadata } - Bolt\Event\Subscriber\TimedPublishSubscriber: - arguments: [ "%bolt.table_prefix%" ] - Bolt\Extension\RoutesLoader: tags: [routing.loader] diff --git a/src/Command/AddUserCommand.php b/src/Command/AddUserCommand.php index 0f29a706..cd6cb114 100644 --- a/src/Command/AddUserCommand.php +++ b/src/Command/AddUserCommand.php @@ -141,7 +141,7 @@ class AddUserCommand extends Command if ($password !== null) { $this->io->text(' > Password: ' . str_repeat('*', mb_strlen($password))); } else { - $passwordQuestion = new Question('Password', Str::generatePassword()); + $passwordQuestion = new Question('Password (input is hidden)', Str::generatePassword()); $passwordQuestion->setHidden(true); $passwordQuestion->setValidator([$this->validator, 'validatePassword']); diff --git a/src/Command/InfoCommand.php b/src/Command/InfoCommand.php index f67e8d05..1b7e6796 100644 --- a/src/Command/InfoCommand.php +++ b/src/Command/InfoCommand.php @@ -58,9 +58,11 @@ HELP $connection = ! empty($platform['connection_status']) ? sprintf('- %s', $platform['connection_status']) : ''; + $tableExists = $this->doctrineVersion->tableContentExists() ? '' : sprintf('- Tables not initialised'); + $io->listing([ sprintf('Install type: %s', Version::installType()), - sprintf('Database: %s %s %s', $platform['driver_name'], $platform['server_version'], $connection), + sprintf('Database: %s %s %s %s', $platform['driver_name'], $platform['server_version'], $connection, $tableExists), sprintf('PHP version: %s', PHP_VERSION), sprintf('Operating System: %s - %s', php_uname('s'), php_uname('r')), ]); diff --git a/src/Command/SetupCommand.php b/src/Command/SetupCommand.php index 8c6a0749..a06119b3 100644 --- a/src/Command/SetupCommand.php +++ b/src/Command/SetupCommand.php @@ -4,6 +4,7 @@ declare(strict_types=1); namespace Bolt\Command; +use Doctrine\DBAL\Connection; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\ArrayInput; use Symfony\Component\Console\Input\InputInterface; @@ -15,6 +16,16 @@ class SetupCommand extends Command { protected static $defaultName = 'bolt:setup'; + /** @var Connection */ + private $connection; + + public function __construct(Connection $connection) + { + $this->connection = $connection; + + parent::__construct(); + } + protected function configure(): void { $this @@ -28,17 +39,21 @@ class SetupCommand extends Command $exitCode = 0; $io = new SymfonyStyle($input, $output); + // Because SQLite breaks on `--if-not-exists`, we need to check for that here. + // See: https://github.com/doctrine/DoctrineBundle/issues/542 + $options = ['-q' => true]; + if ($this->connection->getDatabasePlatform()->getName() !== 'sqlite') { + $options[] = ['--if-not-exists' => true]; + } + $command = $this->getApplication()->find('doctrine:database:create'); - $commandInput = new ArrayInput(['-q' => true]); - $exitCode += $command->run($commandInput, $output); + $exitCode += $command->run(new ArrayInput($options), $output); $command = $this->getApplication()->find('doctrine:schema:create'); - $commandInput = new ArrayInput([]); - $exitCode += $command->run($commandInput, $output); + $exitCode += $command->run(new ArrayInput([]), $output); $command = $this->getApplication()->find('bolt:reset-secret'); - $commandInput = new ArrayInput([]); - $exitCode += $command->run($commandInput, $output); + $exitCode += $command->run(new ArrayInput([]), $output); $command = $this->getApplication()->find('bolt:add-user'); $commandInput = new ArrayInput(['--admin' => true]); diff --git a/src/Doctrine/TablePrefix.php b/src/Doctrine/TablePrefix.php index c12e212c..5b2ccd00 100644 --- a/src/Doctrine/TablePrefix.php +++ b/src/Doctrine/TablePrefix.php @@ -10,11 +10,11 @@ use Doctrine\ORM\Mapping\ClassMetadataInfo; class TablePrefix { - private $prefix; + private $tablePrefix; - public function __construct(string $prefix) + public function __construct(string $tablePrefix) { - $this->prefix = Str::ensureEndsWith($prefix, '_'); + $this->tablePrefix = Str::ensureEndsWith($tablePrefix, '_'); } public function loadClassMetadata(LoadClassMetadataEventArgs $eventArgs): void @@ -23,14 +23,14 @@ class TablePrefix if (! $classMetadata->isInheritanceTypeSingleTable() || $classMetadata->getName() === $classMetadata->rootEntityName) { $classMetadata->setPrimaryTable([ - 'name' => $this->prefix . $classMetadata->getTableName(), + 'name' => $this->tablePrefix . $classMetadata->getTableName(), ]); } foreach ($classMetadata->getAssociationMappings() as $fieldName => $mapping) { if ($mapping['type'] === ClassMetadataInfo::MANY_TO_MANY && $mapping['isOwningSide']) { $mappedTableName = $mapping['joinTable']['name']; - $classMetadata->associationMappings[$fieldName]['joinTable']['name'] = $this->prefix . $mappedTableName; + $classMetadata->associationMappings[$fieldName]['joinTable']['name'] = $this->tablePrefix . $mappedTableName; } } } diff --git a/src/Doctrine/Version.php b/src/Doctrine/Version.php index 4f60f03e..f82f17b8 100644 --- a/src/Doctrine/Version.php +++ b/src/Doctrine/Version.php @@ -4,6 +4,7 @@ declare(strict_types=1); namespace Bolt\Doctrine; +use Bolt\Common\Str; use Doctrine\DBAL\Connection; use Doctrine\DBAL\Driver\PDOConnection; @@ -12,9 +13,13 @@ class Version /** @var Connection */ private $connection; - public function __construct(Connection $connection) + /** @var string */ + private $tablePrefix; + + public function __construct(Connection $connection, string $tablePrefix = 'bolt') { $this->connection = $connection; + $this->tablePrefix = Str::ensureEndsWith($tablePrefix, '_'); } public function getPlatform(): array @@ -37,4 +42,15 @@ class Version 'server_version' => $wrapped->getAttribute(\PDO::ATTR_SERVER_VERSION), ]; } + + public function tableContentExists(): bool + { + try { + $this->connection->executeQuery('SELECT 1 FROM ' . $this->tablePrefix . 'content LIMIT 1; '); + } catch (\Throwable $e) { + return false; + } + + return true; + } } diff --git a/src/Event/Subscriber/TimedPublishSubscriber.php b/src/Event/Subscriber/TimedPublishSubscriber.php index 1167844d..9b9a7a20 100644 --- a/src/Event/Subscriber/TimedPublishSubscriber.php +++ b/src/Event/Subscriber/TimedPublishSubscriber.php @@ -19,12 +19,12 @@ class TimedPublishSubscriber implements EventSubscriberInterface private $entityManager; /** @var string */ - private $prefix; + private $tablePrefix; - public function __construct(string $prefix, EntityManagerInterface $entityManager) + public function __construct(string $tablePrefix, EntityManagerInterface $entityManager) { $this->entityManager = $entityManager; - $this->prefix = Str::ensureEndsWith($prefix, '_'); + $this->tablePrefix = Str::ensureEndsWith($tablePrefix, '_'); } /** @@ -37,8 +37,8 @@ class TimedPublishSubscriber implements EventSubscriberInterface // Publish timed Content records when 'publish_at' has passed and Depublish published Content // records when 'depublish_at' has passed. Note: Placeholders in DBAL don't work for tablenames. - $queryPublish = sprintf('update %scontent SET status = "published", published_at = :now WHERE status = "timed" AND published_at < :now', $this->prefix); - $queryDepublish = sprintf('update %scontent SET status = "held", depublished_at = :now WHERE status = "published" AND depublished_at < :now', $this->prefix); + $queryPublish = sprintf('update %scontent SET status = "published", published_at = :now WHERE status = "timed" AND published_at < :now', $this->tablePrefix); + $queryDepublish = sprintf('update %scontent SET status = "held", depublished_at = :now WHERE status = "published" AND depublished_at < :now', $this->tablePrefix); try { $conn->executeUpdate($queryPublish, [':now' => $now]);