Completed
Push — master ( 4a905b...82874d )
by Michael
02:55
created

SchemaUpdater::dropDatabaseProcedure()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 8
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 8
ccs 0
cts 8
cp 0
rs 9.4285
cc 1
eloc 6
nc 1
nop 1
crap 2
1
<?php
2
declare(strict_types = 1);
3
/**
4
 * Contains SchemaUpdater class.
5
 *
6
 * PHP version 7.0+
7
 *
8
 * LICENSE:
9
 * This file is part of Yet Another Php Eve Api Library also know as Yapeal
10
 * which can be used to access the Eve Online API data and place it into a
11
 * database.
12
 * Copyright (C) 2014-2016 Michael Cummings
13
 *
14
 * This program is free software: you can redistribute it and/or modify it
15
 * under the terms of the GNU Lesser General Public License as published by the
16
 * Free Software Foundation, either version 3 of the License, or (at your
17
 * option) any later version.
18
 *
19
 * This program is distributed in the hope that it will be useful, but WITHOUT
20
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
21
 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
22
 * for more details.
23
 *
24
 * You should have received a copy of the GNU Lesser General Public License
25
 * along with this program. If not, see
26
 * <http://spdx.org/licenses/LGPL-3.0.html>.
27
 *
28
 * You should be able to find a copy of this license in the COPYING-LESSER.md
29
 * file. A copy of the GNU GPL should also be available in the COPYING.md file.
30
 *
31
 * @copyright 2014-2016 Michael Cummings
32
 * @license   http://www.gnu.org/copyleft/lesser.html GNU LGPL
33
 * @author    Michael Cummings <[email protected]>
34
 */
35
namespace Yapeal\Cli\Schema;
36
37
use Symfony\Component\Console\Output\OutputInterface;
38
use Yapeal\Container\ContainerInterface;
39
use Yapeal\Event\YEMAwareTrait;
40
use Yapeal\Exception\YapealDatabaseException;
41
use Yapeal\Log\Logger;
42
43
/**
44
 * Class SchemaUpdater
45
 */
46
class SchemaUpdater extends AbstractSchemaCommon
47
{
48
    use YEMAwareTrait;
49
    /**
50
     * @param string             $name
51
     * @param ContainerInterface $dic
52
     *
53
     * @throws \Symfony\Component\Console\Exception\InvalidArgumentException
54
     * @throws \Symfony\Component\Console\Exception\LogicException
55
     */
56
    public function __construct(string $name, ContainerInterface $dic)
57
    {
58
        $this->setDescription('Retrieves SQL from files and updates schema');
59
        $this->setName($name);
60
        $this->setDic($dic);
61
        parent::__construct($name);
62
    }
63
    /**
64
     * @param OutputInterface $output
65
     *
66
     * @throws \DomainException
67
     * @throws \InvalidArgumentException
68
     * @throws \LogicException
69
     * @throws \Symfony\Component\Console\Exception\LogicException
70
     * @throws \Yapeal\Exception\YapealDatabaseException
71
     */
72
    protected function addDatabaseProcedure(OutputInterface $output)
73
    {
74
        $name = 'DatabaseUpdater::addDatabaseProcedure';
75
        $csq = $this->getCsq();
76
        $this->executeSqlStatements($csq->getDropAddOrModifyColumnProcedure()
77
            . PHP_EOL
78
            . $csq->getCreateAddOrModifyColumnProcedure(),
79
            $name,
80
            $output);
81
        $output->writeln('');
82
    }
83
    /**
84
     * Configures the current command.
85
     *
86
     * @throws \Symfony\Component\Console\Exception\InvalidArgumentException
87
     */
88
    protected function configure()
89
    {
90
        $help = <<<'HELP'
91
The <info>%command.full_name%</info> command is used to initialize (create) a new
92
 schema and tables to be used by Yapeal-ng. If you already have a
93
 config/yapeal.yaml file setup you can use the following:
94
95
    <info>php %command.full_name%</info>
96
97
EXAMPLES:
98
To use a configuration file in a different location:
99
    <info>%command.name% -c /my/very/special/config.yaml</info>
100
101
<info>NOTE:</info>
102
Only the Sql section of the configuration file will be used.
103
104
You can also use the command before setting up a configuration file like so:
105
    <info>%command.name% -o "localhost" -d "yapeal" -u "YapealUser" -p "secret"
106
107
HELP;
108
        $this->addOptions($help);
109
        $this->setAliases(['Database:Update']);
110
    }
111
    /**
112
     * @param OutputInterface $output
113
     *
114
     * @throws \DomainException
115
     * @throws \InvalidArgumentException
116
     * @throws \LogicException
117
     * @throws \Symfony\Component\Console\Exception\LogicException
118
     * @throws \Yapeal\Exception\YapealDatabaseException
119
     */
120
    protected function dropDatabaseProcedure(OutputInterface $output)
121
    {
122
        $name = 'DatabaseUpdater::dropDatabaseProcedure';
123
        $this->executeSqlStatements($this->getCsq()
124
            ->getDropAddOrModifyColumnProcedure(),
125
            $name,
126
            $output);
127
    }
128
    /**
129
     * @param OutputInterface $output
130
     *
131
     * @return string
132
     * @throws \LogicException
133
     * @throws \Yapeal\Exception\YapealDatabaseException
134
     */
135
    protected function getLatestDatabaseVersion(OutputInterface $output): string
136
    {
137
        $sql = $this->getCsq()
138
            ->getUtilLatestDatabaseVersion();
139
        try {
140
            $result = $this->getPdo()
141
                ->query($sql, \PDO::FETCH_NUM);
142
            $version = sprintf('%018.3F', $result->fetchColumn());
143
            $result->closeCursor();
144
        } catch (\PDOException $exc) {
145
            $version = '19700101000001.000';
146
            $mess = sprintf('<error>Could NOT get latest database version using default %1$s</error>', $version);
147
            $output->writeln([$sql, $mess]);
148
            $mess = sprintf('<info>Error message from database connection was %s</info>',
149
                $exc->getMessage());
150
            $output->writeln($mess);
151
        }
152
        return $version;
153
    }
154
    /**
155
     * @return array
156
     * @throws \LogicException
157
     */
158
    protected function getReplacements()
159
    {
160
        $replacements = parent::getReplacements();
161
        $replacements['$$'] = ';';
162
        return $replacements;
163
    }
164
    /**
165
     * @param OutputInterface $output
166
     *
167
     * @return string[]
168
     * @throws \LogicException
169
     */
170
    protected function getUpdateFileList(OutputInterface $output): array
171
    {
172
        $fileNames = [];
173
        $path = $this->getDic()['Yapeal.Sql.dir'] . 'updates/';
174
        if (!is_readable($path) || !is_dir($path)) {
175
            $mess = sprintf('<info>Could NOT access update directory %1$s</info>',
176
                $path);
177
            $output->writeln($mess);
178
            return $fileNames;
179
        }
180
        foreach (new \DirectoryIterator($path) as $fileInfo) {
181
            if ($fileInfo->isDot() || $fileInfo->isDir()) {
182
                continue;
183
            }
184
            if ('sql' !== $fileInfo->getExtension()) {
185
                continue;
186
            }
187
            $fileNames[] = $this->getFpn()
188
                ->normalizeFile($fileInfo->getPathname());
189
        }
190
        asort($fileNames);
191
        return $fileNames;
192
    }
193
    /**
194
     * @param OutputInterface $output
195
     *
196
     * @throws \DomainException
197
     * @throws \InvalidArgumentException
198
     * @throws \LogicException
199
     * @throws \Symfony\Component\Console\Exception\LogicException
200
     * @throws \Yapeal\Exception\YapealDatabaseException
201
     */
202
    protected function processSql(OutputInterface $output)
203
    {
204
        if (!$this->hasYem()) {
205
            $this->setYem($this->getDic()['Yapeal.Event.Mediator']);
206
        }
207
        $yem = $this->getYem();
208
        $this->addDatabaseProcedure($output);
209
        foreach ($this->getUpdateFileList($output) as $fileName) {
210
            /** @noinspection DisconnectedForeachInstructionInspection */
211
            $latestVersion = $this->getLatestDatabaseVersion($output);
212
            if (!is_file($fileName)) {
213
                if ($output::VERBOSITY_QUIET !== $output->getVerbosity()) {
214
                    $mess = sprintf('<info>Could NOT find SQL file %1$s</info>', $fileName);
215
                    $yem->triggerLogEvent('Yapeal.Log.log', Logger::INFO, strip_tags($mess));
216
                    $output->writeln($mess);
217
                }
218
                continue;
219
            }
220
            $updateVersion = basename($fileName, '.sql');
221
            if ($updateVersion <= $latestVersion) {
222
                if ($output::VERBOSITY_QUIET !== $output->getVerbosity()) {
223
                    $mess = sprintf('<info>Skipping SQL file %1$s since its <= the latest database version %2$s</info>',
224
                        basename($fileName),
225
                        $latestVersion);
226
                    $yem->triggerLogEvent('Yapeal.Log.log', Logger::INFO, strip_tags($mess));
227
                    $output->writeln($mess);
228
                }
229
                continue;
230
            }
231
            $sqlStatements = file_get_contents($fileName);
232
            if (false === $sqlStatements) {
233
                if ($output::VERBOSITY_QUIET !== $output->getVerbosity()) {
234
                    $mess = sprintf('<error>Could NOT get contents of SQL file %1$s</error>',
235
                        $fileName);
236
                    $yem->triggerLogEvent('Yapeal.Log.log', Logger::INFO, strip_tags($mess));
237
                    $output->writeln($mess);
238
                }
239
                continue;
240
            }
241
            $this->executeSqlStatements($sqlStatements, $fileName, $output);
242
            $this->updateDatabaseVersion($updateVersion);
243
        }
244
        $this->dropDatabaseProcedure($output);
245
    }
246
    /**
247
     * @param string $updateVersion
248
     *
249
     * @return SchemaUpdater
250
     * @throws \LogicException
251
     * @throws \Yapeal\Exception\YapealDatabaseException
252
     */
253
    protected function updateDatabaseVersion(string $updateVersion)
254
    {
255
        $pdo = $this->getPdo();
256
        $sql = $this->getCsq()
257
            ->getUtilLatestDatabaseVersionUpdate();
258
        try {
259
            $pdo->beginTransaction();
260
            $pdo->prepare($sql)
261
                ->execute([$updateVersion]);
262
            $pdo->commit();
263
        } catch (\PDOException $exc) {
264
            $mess = $sql . PHP_EOL;
265
            $mess .= sprintf('Database error message was %s', $exc->getMessage()) . PHP_EOL;
266
            $mess .= sprintf('Database "version" update failed for %1$s',
267
                $updateVersion);
268
            if ($this->getPdo()
269
                ->inTransaction()
270
            ) {
271
                $this->getPdo()
272
                    ->rollBack();
273
            }
274
            throw new YapealDatabaseException($mess, 2);
275
        }
276
        return $this;
277
    }
278
}
279