Completed
Push — master ( 75748b...36048b )
by Michael
06:58
created

DatabaseUpdater::processSql()   C

Complexity

Conditions 8
Paths 8

Size

Total Lines 40
Code Lines 29

Duplication

Lines 0
Ratio 0 %

Importance

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