Completed
Push — master ( b659c5...c65770 )
by Michael
10:22
created

SchemaCreator::execute()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 17
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 12

Importance

Changes 0
Metric Value
dl 0
loc 17
ccs 0
cts 14
cp 0
rs 9.4285
c 0
b 0
f 0
cc 3
eloc 10
nc 3
nop 2
crap 12
1
<?php
2
declare(strict_types = 1);
3
/**
4
 * Contains SchemaCreator 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-2017 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-2017 Michael Cummings
32
 * @license   LGPL-3.0+
33
 * @author    Michael Cummings <[email protected]>
34
 */
35
namespace Yapeal\Console\Schema;
36
37
use Symfony\Component\Console\Input\InputInterface;
38
use Symfony\Component\Console\Input\InputOption;
39
use Symfony\Component\Console\Output\OutputInterface;
40
use Symfony\Component\Console\Question\ConfirmationQuestion;
41
use Yapeal\Container\ContainerInterface;
42
use Yapeal\Event\YEMAwareTrait;
43
use Yapeal\Exception\YapealDatabaseException;
44
use Yapeal\Log\Logger;
45
use Yapeal\Sql\CSQAwareTrait;
46
47
/**
48
 * Class SchemaCreator
49
 */
50
class SchemaCreator extends AbstractSchemaCommon
51
{
52
    use CSQAwareTrait;
53
    use YEMAwareTrait;
54
    /**
55
     * @param string             $name
56
     * @param ContainerInterface $dic
57
     *
58
     * @throws \InvalidArgumentException
59
     * @throws \Symfony\Component\Console\Exception\LogicException
60
     */
61
    public function __construct(string $name, ContainerInterface $dic)
62
    {
63
        $this->setDescription('Retrieves SQL from files and initializes schema');
64
        $this->setDic($dic);
65
        $this->sqlSubs = $sqlSubs = $dic['Yapeal.Sql.Callable.GetSqlMergedSubs'];
66
        $this->platform = $sqlSubs['{platform}'];
67
        $this->schemaName = $sqlSubs['{schema}'];
68
        $this->createDirs = [$sqlSubs['{dir}']];
69
        if (!empty($sqlSubs['{appDir}'])) {
70
            $this->createDirs[] = $sqlSubs['{appDir}'];
71
        }
72
        $this->setCsq($dic['Yapeal.Sql.Callable.CommonQueries']);
73
        $this->setPdo($dic['Yapeal.Sql.Callable.Connection']);
74
        $this->setYem($dic['Yapeal.Event.Callable.Mediator']);
75
        parent::__construct($name);
76
    }
77
    /**
78
     * Configures the current command.
79
     */
80
    protected function configure()
81
    {
82
        $help = <<<'HELP'
83
The <info>%command.name%</info> command is used to create (initialize) a
84
new schema and tables to be used by Yapeal-ng. If you already have a
85
config/yapeal.yaml file setup you should be able to use the following:
86
    <info>php bin/yc %command.name%</info>
87
from the directory where it is installed. If you have required Yapeal-ng in
88
your application with Composer you'll find it in the vendor/bin/yc directory.
89
90
EXAMPLES:
91
These examples assume you are in the base directory of your application where
92
your composer.json file is found.
93
94
To use a configuration file in a different location:
95
    <info>./vendor/bin/yc %command.name% -c /my/very/special/config.yaml</info>
96
You can also use the command before setting up a configuration file like so:
97
    <info>./vendor/bin/yc %command.name% -o "localhost" -s "yapeal-ng" -u "YapealUser" -p "secret" -l "mysql"</info>
98
99
Windows users can use ./vendor/bin/yc.bat in place of ./vendor/bin/yc above.
100
101
Finally you can use the <comment>VERY DANGEROUS</comment> '--dropSchema' option to also drop an
102
exist schema and all it's tables and their data before re-creating everything.
103
Make sure you have a good backup of your schema(database) before using this
104
option.
105
HELP;
106
        $this->addOptions($help);
107
        $desc = 'Drop existing schema(database) before re-creating.'
108
            . ' <comment>Warning all the tables will be dropped as well!</comment>';
109
        $this->addOption('dropSchema', null, InputOption::VALUE_NONE, $desc);
110
        $this->setAliases(['Database:Init']);
111
    }
112
    /**
113
     * Executes the current command.
114
     *
115
     * This method is not abstract because you can use this class
116
     * as a concrete class. In this case, instead of defining the
117
     * execute() method, you set the code to execute by passing
118
     * a Closure to the setCode() method.
119
     *
120
     * @param InputInterface  $input  An InputInterface instance
121
     * @param OutputInterface $output An OutputInterface instance
122
     *
123
     * @return int null or 0 if everything went fine, or an error code
124
     *
125
     * @throws \DomainException
126
     * @throws \InvalidArgumentException
127
     * @throws \LogicException
128
     * @throws \Symfony\Component\Console\Exception\InvalidArgumentException
129
     * @throws \Symfony\Component\Console\Exception\LogicException
130
     * @throws \Symfony\Component\Console\Exception\RuntimeException
131
     * @throws \UnexpectedValueException
132
     * @see    setCode()
133
     */
134
    protected function execute(InputInterface $input, OutputInterface $output): int
135
    {
136
        if ($input->getOption('dropSchema')) {
137
            /**
138
             * @var \Symfony\Component\Console\Helper\QuestionHelper $question
139
             */
140
            $question = $this->getHelper('question');
141
            $mess = '<comment>Are you sure you want to drop the schema(database)'
142
                . ' and it\'s tables with their data?(no)</comment>';
143
            $confirm = new ConfirmationQuestion($mess, false);
144
            $this->dropSchema = (bool)$question->ask($input, $output, $confirm);
145
            if (!$this->dropSchema) {
146
                $output->writeln('<info>Ignoring drop schema(database)</info>');
147
            }
148
        }
149
        return parent::execute($input, $output);
150
    }
151
    /**
152
     * @param OutputInterface $output
153
     *
154
     * @throws \DomainException
155
     * @throws \InvalidArgumentException
156
     * @throws \LogicException
157
     * @throws \Symfony\Component\Console\Exception\LogicException
158
     * @throws \UnexpectedValueException
159
     * @throws \Yapeal\Exception\YapealDatabaseException
160
     */
161
    protected function processSql(OutputInterface $output)
162
    {
163
        $yem = $this->getYem();
164
        if ($this->dropSchema) {
165
            $this->dropIfSchemaExist($output);
166
        }
167
        $fileNames = $this->getCreateFileNames();
168
        if (0 === count($fileNames)) {
169
            $mess = '<error>No SQL create files were found</error>';
170
            $yem->triggerLogEvent('Yapeal.Log.log', Logger::ERROR, strip_tags($mess));
171
            if ($output::VERBOSITY_QUIET !== $output->getVerbosity()) {
172
                $output->writeln($mess);
173
            }
174
            return;
175
        }
176
        foreach ($fileNames as $fileName) {
177
            if (false === $sqlStatements = $this->safeFileRead($fileName)) {
178
                $mess = sprintf('<comment>Could NOT get contents of SQL file for %s</comment>', $fileName);
179
                $yem->triggerLogEvent('Yapeal.Log.log', Logger::DEBUG, strip_tags($mess));
180
                if ($output::VERBOSITY_DEBUG <= $output->getVerbosity()) {
181
                    $output->writeln($mess);
182
                }
183
                continue;
184
            }
185
            $this->executeSqlStatements($sqlStatements, $fileName, $output);
186
        }
187
    }
188
    /**
189
     * @param OutputInterface $output
190
     *
191
     * @throws YapealDatabaseException
192
     * @throws \DomainException
193
     * @throws \InvalidArgumentException
194
     * @throws \LogicException
195
     * @throws \UnexpectedValueException
196
     */
197
    private function dropIfSchemaExist(OutputInterface $output)
198
    {
199
        $csq = $this->getCsq();
200
        $yem = $this->getYem();
201
        $pdo = $this->getPdo();
202
        $sql = $csq->getSchemaNames();
203
        $yem->triggerLogEvent('Yapeal.Log.log', Logger::DEBUG, 'sql - ' . $sql);
204
        try {
205
            $stmt = $pdo->query($sql);
206
            $schemas = $stmt->fetchAll(\PDO::FETCH_COLUMN);
207
        } catch (\PDOException $exc) {
208
            $mess = '<error>Failed to get Schema list</error>';
209
            $output->writeln($mess);
210
            throw new YapealDatabaseException(strip_tags($mess), 2, $exc);
211
        }
212
        if (!in_array($this->schemaName, $schemas)) {
213
            return;
214
        }
215
        $sql = $csq->getDropSchema();
216
        $yem->triggerLogEvent('Yapeal.Log.log', Logger::DEBUG, 'sql - ' . $sql);
217
        try {
218
            $pdo->exec($sql);
219
        } catch (\PDOException $exc) {
220
            $mess = '<error>Failed to drop Schema</error>';
221
            $output->writeln($mess);
222
            throw new YapealDatabaseException(strip_tags($mess), 2, $exc);
223
        }
224
    }
225
    /**
226
     * @return array|string[]
227
     */
228
    private function getCreateFileNames(): array
229
    {
230
        $fileExt = sprintf('.%s.sql', $this->platform);
231
        $globPath = sprintf('{%1$s}Create/{*%2$s,*/*%2$s}',
232
            implode(',', $this->createDirs),
233
            $fileExt);
234
        return glob($globPath, GLOB_BRACE | GLOB_NOESCAPE);
235
    }
236
    /**
237
     * @var string[] $createDirs
238
     */
239
    private $createDirs;
240
    /**
241
     * @var bool $dropSchema
242
     */
243
    private $dropSchema = false;
244
    /**
245
     * @var string $platform
246
     */
247
    private $platform;
248
    /**
249
     * @var string $schemaName
250
     */
251
    private $schemaName;
252
}
253