Completed
Push — master ( 94e1e6...1341d4 )
by Alejandro
16s queued 11s
created

CreateDatabaseCommand::lockedExecute()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 17
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
eloc 9
c 2
b 0
f 0
dl 0
loc 17
rs 9.9666
cc 2
nc 2
nop 2
1
<?php
2
declare(strict_types=1);
3
4
namespace Shlinkio\Shlink\CLI\Command\Db;
5
6
use Doctrine\DBAL\Connection;
7
use Shlinkio\Shlink\CLI\Command\Util\LockedCommandConfig;
8
use Shlinkio\Shlink\CLI\Util\ExitCodes;
9
use Symfony\Component\Console\Helper\ProcessHelper;
10
use Symfony\Component\Console\Input\InputInterface;
11
use Symfony\Component\Console\Output\OutputInterface;
12
use Symfony\Component\Console\Style\SymfonyStyle;
13
use Symfony\Component\Lock\Factory as Locker;
14
use Symfony\Component\Process\PhpExecutableFinder;
15
16
use function Functional\contains;
17
18
class CreateDatabaseCommand extends AbstractDatabaseCommand
19
{
20
    public const NAME = 'db:create';
21
    public const DOCTRINE_HELPER_SCRIPT = 'vendor/doctrine/orm/bin/doctrine.php';
22
    public const DOCTRINE_HELPER_COMMAND = 'orm:schema-tool:create';
23
24
    /** @var Connection */
25
    private $regularConn;
26
    /** @var Connection */
27
    private $noDbNameConn;
28
29
    public function __construct(
30
        Locker $locker,
31
        ProcessHelper $processHelper,
32
        PhpExecutableFinder $phpFinder,
33
        Connection $conn,
34
        Connection $noDbNameConn
35
    ) {
36
        parent::__construct($locker, $processHelper, $phpFinder);
37
        $this->regularConn = $conn;
38
        $this->noDbNameConn = $noDbNameConn;
39
    }
40
41
    protected function configure(): void
42
    {
43
        $this
44
            ->setName(self::NAME)
45
            ->setDescription(
46
                'Creates the database needed for shlink to work. It will do nothing if the database already exists'
47
            );
48
    }
49
50
    protected function lockedExecute(InputInterface $input, OutputInterface $output): int
51
    {
52
        $io = new SymfonyStyle($input, $output);
53
54
        $this->checkDbExists();
55
56
        if ($this->schemaExists()) {
57
            $io->success('Database already exists. Run "db:migrate" command to make sure it is up to date.');
58
            return ExitCodes::EXIT_SUCCESS;
59
        }
60
61
        // Create database
62
        $io->writeln('<fg=blue>Creating database tables...</>');
63
        $this->runPhpCommand($output, [self::DOCTRINE_HELPER_SCRIPT, self::DOCTRINE_HELPER_COMMAND]);
64
        $io->success('Database properly created!');
65
66
        return ExitCodes::EXIT_SUCCESS;
67
    }
68
69
    private function checkDbExists(): void
70
    {
71
        if ($this->regularConn->getDatabasePlatform()->getName() === 'sqlite') {
72
            return;
73
        }
74
75
        // In order to create the new database, we have to use a connection where the dbname was not set.
76
        // Otherwise, it will fail to connect and will not be able to create the new database
77
        $schemaManager = $this->noDbNameConn->getSchemaManager();
78
        $databases = $schemaManager->listDatabases();
79
        $shlinkDatabase = $this->regularConn->getDatabase();
80
81
        if (! contains($databases, $shlinkDatabase)) {
82
            $schemaManager->createDatabase($shlinkDatabase);
83
        }
84
    }
85
86
    private function schemaExists(): bool
87
    {
88
        // If at least one of the shlink tables exist, we will consider the database exists somehow.
89
        // Any inconsistency will be taken care by the migrations
90
        $schemaManager = $this->regularConn->getSchemaManager();
91
        return ! empty($schemaManager->listTableNames());
92
    }
93
94
    protected function getLockConfig(): LockedCommandConfig
95
    {
96
        return new LockedCommandConfig($this->getName(), true);
97
    }
98
}
99