Completed
Push — ezp-29938-install-schema-using... ( 1a7e83 )
by
unknown
95:17 queued 56:12
created

getDropSqlStatementsForExistingSchema()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 17

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
nc 3
nop 2
dl 0
loc 17
rs 9.7
c 0
b 0
f 0
1
<?php
2
3
/**
4
 * This file is part of the eZ Publish Kernel package.
5
 *
6
 * @copyright Copyright (C) eZ Systems AS. All rights reserved.
7
 * @license For full copyright and license information view LICENSE file distributed with this source code.
8
 */
9
namespace EzSystems\PlatformInstallerBundle\Installer;
10
11
use Doctrine\DBAL\Connection;
12
use Doctrine\DBAL\DBALException;
13
use Doctrine\DBAL\Platforms\AbstractPlatform;
14
use Doctrine\DBAL\Schema\Schema;
15
use EzSystems\DoctrineSchema\API\Builder\SchemaBuilder;
16
use Symfony\Component\Console\Helper\ProgressBar;
17
use Symfony\Component\Filesystem\Filesystem;
18
use eZ\Publish\Core\Base\Exceptions\InvalidArgumentException;
19
20
class DbBasedInstaller
21
{
22
    /**
23
     * @var \Doctrine\DBAL\Connection
24
     */
25
    protected $db;
26
27
    /**
28
     * @var \Symfony\Component\Console\Output\OutputInterface
29
     */
30
    protected $output;
31
32
    /**
33
     * @var string
34
     */
35
    protected $baseDataDir;
36
37
    /**
38
     * @var \EzSystems\DoctrineSchema\API\Builder\SchemaBuilder|null
39
     */
40
    protected $schemaBuilder;
41
42
    public function __construct(Connection $db, ?SchemaBuilder $schemaBuilder = null)
43
    {
44
        $this->db = $db;
45
        $this->schemaBuilder = $schemaBuilder;
46
47
        // parametrized so other installer implementations can override this
48
        $this->baseDataDir = __DIR__ . '/../../../../../data';
49
    }
50
51
    /**
52
     * @param \Symfony\Component\Console\Output\OutputInterface $output
53
     */
54
    public function setOutput($output)
55
    {
56
        $this->output = $output;
57
    }
58
59
    /**
60
     * Import Schema using event-driven Schema Builder API from eZ Systems DoctrineSchema Bundle.
61
     *
62
     * If you wish to extend schema, implement your own EventSubscriber
63
     *
64
     * @see \EzSystems\DoctrineSchema\API\Builder\SchemaBuilderEvent
65
     * @see \EzSystems\PlatformInstallerBundle\Event\Subscriber\BuildSchemaSubscriber
66
     *
67
     * @throws \Doctrine\DBAL\DBALException
68
     */
69
    public function importSchema()
70
    {
71
        // note: schema is built using Schema Builder event-driven API
72
        $schema = $this->schemaBuilder->buildSchema();
73
        $databasePlatform = $this->db->getDatabasePlatform();
74
        $queries = array_merge(
75
            $this->getDropSqlStatementsForExistingSchema($schema, $databasePlatform),
76
            // generate schema DDL queries
77
            $schema->toSql($databasePlatform)
78
        );
79
80
        $queriesCount = count($queries);
81
        $this->output->writeln(
82
            sprintf(
83
                '<info>Executing %d queries on database <comment>%s</comment> (<comment>%s</comment>)</info>',
84
                $queriesCount,
85
                $this->db->getDatabase(),
86
                $this->db->getDatabasePlatform()->getName()
87
            )
88
        );
89
        $progressBar = new ProgressBar($this->output);
90
        $progressBar->start($queriesCount);
91
92
        try {
93
            $this->db->beginTransaction();
94
            foreach ($queries as $query) {
95
                $this->db->exec($query);
96
                $progressBar->advance(1);
97
            }
98
            $this->db->commit();
99
        } catch (DBALException $e) {
100
            $this->db->rollBack();
101
            throw $e;
102
        }
103
104
        $progressBar->finish();
105
        // go to the next line after ProgressBar::finish
106
        $this->output->writeln('');
107
    }
108
109
    /**
110
     * @param \Doctrine\DBAL\Schema\Schema $newSchema
111
     * @param \Doctrine\DBAL\Platforms\AbstractPlatform $databasePlatform
112
     *
113
     * @return string[]
114
     */
115
    protected function getDropSqlStatementsForExistingSchema(
116
        Schema $newSchema,
117
        AbstractPlatform $databasePlatform
118
    ): array {
119
        $existingSchema = $this->db->getSchemaManager()->createSchema();
120
        $statements = [];
121
        // reverse table order for clean-up (due to FKs)
122
        $tables = array_reverse($newSchema->getTables());
123
        // cleanup pre-existing database
124
        foreach ($tables as $table) {
125
            if ($existingSchema->hasTable($table->getName())) {
126
                $statements[] = $databasePlatform->getDropTableSQL($table);
127
            }
128
        }
129
130
        return $statements;
131
    }
132
133
    /**
134
     * Copy and override configuration file.
135
     *
136
     * @param string $source
137
     * @param string $target
138
     */
139
    protected function copyConfigurationFile($source, $target)
140
    {
141
        $fs = new Filesystem();
142
        $fs->copy($source, $target, true);
143
144
        if (!$this->output->isQuiet()) {
145
            $this->output->writeln("Copied $source to $target");
146
        }
147
    }
148
149
    protected function runQueriesFromFile($file)
150
    {
151
        $queries = array_filter(preg_split('(;\\s*$)m', file_get_contents($file)));
152
153
        if (!$this->output->isQuiet()) {
154
            $this->output->writeln(
155
                sprintf(
156
                    '<info>Executing %d queries from <comment>%s</comment> on database <comment>%s</comment></info>',
157
                    count($queries),
158
                    $file,
159
                    $this->db->getDatabase()
160
                )
161
            );
162
        }
163
164
        foreach ($queries as $query) {
165
            $this->db->exec($query);
166
        }
167
    }
168
169
    /**
170
     * Get DBMS-specific SQL data file path.
171
     *
172
     * @param string $relativeFilePath SQL file path relative to {baseDir}/{dbms} directory
173
     *
174
     * @return string absolute existing file path
175
     *
176
     * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException
177
     *
178
     * @throws \Doctrine\DBAL\DBALException
179
     *
180
     * @since 6.13
181
     */
182
    final protected function getKernelSQLFileForDBMS($relativeFilePath)
183
    {
184
        $databasePlatform = $this->db->getDatabasePlatform()->getName();
185
        $filePath = "{$this->baseDataDir}/{$databasePlatform}/{$relativeFilePath}";
186
187
        if (!is_readable($filePath)) {
188
            throw new InvalidArgumentException(
189
                '$relativeFilePath',
190
                sprintf(
191
                    'DBMS-specific file for %s database platform does not exist or is not readable: %s',
192
                    $databasePlatform,
193
                    $filePath
194
                )
195
            );
196
        }
197
198
        // apply realpath for more user-friendly Console output
199
        return realpath($filePath);
200
    }
201
}
202