Completed
Push — master ( e502a6...b659c5 )
by Michael
08:23
created

SchemaCreator::addCustomFile()   B

Complexity

Conditions 5
Paths 8

Size

Total Lines 22
Code Lines 15

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 30

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 22
ccs 0
cts 21
cp 0
rs 8.6737
cc 5
eloc 15
nc 8
nop 3
crap 30
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-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\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\Log\Logger;
44
45
/**
46
 * Class SchemaCreator
47
 */
48
class SchemaCreator extends AbstractSchemaCommon
49
{
50
    use YEMAwareTrait;
51
    /**
52
     * @param string             $name
53
     * @param ContainerInterface $dic
54
     *
55
     * @throws \Symfony\Component\Console\Exception\LogicException
56
     */
57
    public function __construct(string $name, ContainerInterface $dic)
58
    {
59
        $this->setDescription('Retrieves SQL from files and initializes schema');
60
        $this->setDic($dic);
61
        $this->platform = $dic['Yapeal.Sql.platform'];
62
        $this->createDirs = [$dic['Yapeal.Sql.dir']];
63
        if (!empty($dic['Yapeal.Sql.appDir'])) {
64
            $this->createDirs[] = $dic['Yapeal.Sql.appDir'];
65
        }
66
        $this->setPdo($dic['Yapeal.Sql.Connection']);
67
        $this->setYem($dic['Yapeal.Event.Mediator']);
68
        parent::__construct($name);
69
    }
70
    /**
71
     * Configures the current command.
72
     */
73
    protected function configure()
74
    {
75
        $help = <<<'HELP'
76
The <info>%command.name%</info> command is used to create (initialize) a
77
new schema and tables to be used by Yapeal-ng. If you already have a
78
config/yapeal.yaml file setup you should be able to use the following:
79
    <info>php bin/yc %command.name%</info>
80
from the directory where it is installed. If you have required Yapeal-ng in
81
your application with Composer you'll find it in the vendor/bin/yc directory.
82
83
EXAMPLES:
84
These examples assume you are in the base directory of your application where
85
your composer.json file is found.
86
87
To use a configuration file in a different location:
88
    <info>./vendor/bin/yc %command.name% -c /my/very/special/config.yaml</info>
89
You can also use the command before setting up a configuration file like so:
90
    <info>./vendor/bin/yc %command.name% -o "localhost" -d "yapeal-ng" -u "YapealUser" -p "secret" -l "mysql"</info>
91
92
Windows users can use ./vendor/bin/yc.bat in place of ./vendor/bin/yc above.
93
94
Finally you can use the <comment>VERY DANGEROUS</comment> '--dropSchema' option to also drop an
95
exist schema and all it's tables and their data before re-creating everything.
96
Make sure you have a good backup of your schema(database) before using this
97
option.
98
HELP;
99
        $this->addOptions($help);
100
        $desc = 'Drop existing schema(database) before re-creating.'
101
            . ' <comment>Warning all the tables will be dropped as well!</comment>';
102
        $this->addOption('dropSchema', null, InputOption::VALUE_NONE, $desc);
103
        $this->setAliases(['Database:Init']);
104
    }
105
    /**
106
     * Executes the current command.
107
     *
108
     * This method is not abstract because you can use this class
109
     * as a concrete class. In this case, instead of defining the
110
     * execute() method, you set the code to execute by passing
111
     * a Closure to the setCode() method.
112
     *
113
     * @param InputInterface  $input  An InputInterface instance
114
     * @param OutputInterface $output An OutputInterface instance
115
     *
116
     * @return int null or 0 if everything went fine, or an error code
117
     *
118
     * @throws \DomainException
119
     * @throws \InvalidArgumentException
120
     * @throws \LogicException
121
     * @throws \Symfony\Component\Console\Exception\InvalidArgumentException
122
     * @throws \Symfony\Component\Console\Exception\LogicException
123
     * @throws \Symfony\Component\Console\Exception\RuntimeException
124
     * @throws \UnexpectedValueException
125
     * @see    setCode()
126
     */
127
    protected function execute(InputInterface $input, OutputInterface $output): int
128
    {
129
        if ($input->getOption('dropSchema')) {
130
            /**
131
             * @var \Symfony\Component\Console\Helper\QuestionHelper $question
132
             */
133
            $question = $this->getHelper('question');
134
            $mess = '<comment>Are you sure you want to drop the schema(database)'
135
                . ' and it\'s tables with their data?(no)</comment>';
136
            $confirm = new ConfirmationQuestion($mess, false);
137
            $this->dropSchema = (bool)$question->ask($input, $output, $confirm);
138
            if (!$this->dropSchema) {
139
                $output->writeln('<info>Ignoring drop schema(database)</info>');
140
            }
141
        }
142
        return parent::execute($input, $output);
143
    }
144
    /**
145
     * @param OutputInterface $output
146
     *
147
     * @throws \DomainException
148
     * @throws \InvalidArgumentException
149
     * @throws \LogicException
150
     * @throws \Symfony\Component\Console\Exception\LogicException
151
     * @throws \UnexpectedValueException
152
     * @throws \Yapeal\Exception\YapealDatabaseException
153
     */
154
    protected function processSql(OutputInterface $output)
155
    {
156
        $yem = $this->getYem();
157
        $fileNames = $this->getCreateFileNames();
158
        if (0 === count($fileNames)) {
159
            $mess = '<error>No SQL create files were found</error>';
160
            $yem->triggerLogEvent('Yapeal.Log.log', Logger::ERROR, strip_tags($mess));
161
            if ($output::VERBOSITY_QUIET !== $output->getVerbosity()) {
162
                $output->writeln($mess);
163
            }
164
            return;
165
        }
166
        foreach ($fileNames as $fileName) {
167
            if (false === $sqlStatements = $this->safeFileRead($fileName)) {
168
                $mess = sprintf('<comment>Could NOT get contents of SQL file for %s</comment>', $fileName);
169
                $yem->triggerLogEvent('Yapeal.Log.log', Logger::DEBUG, strip_tags($mess));
170
                if ($output::VERBOSITY_DEBUG <= $output->getVerbosity()) {
171
                    $output->writeln($mess);
172
                }
173
                continue;
174
            }
175
            $this->executeSqlStatements($sqlStatements, $fileName, $output);
176
        }
177
    }
178
    /**
179
     * @return array|string[]
180
     */
181
    private function getCreateFileNames(): array
182
    {
183
        $fileExt = sprintf('.%s.sql', $this->platform);
184
        $globPath = sprintf('{%1$s}Create/{*%2$s,*/*%2$s}',
185
            implode(',', $this->createDirs),
186
            $fileExt);
187
        $regex = '%^.+?/\w*\..+$%';
188
        $drop = $this->dropSchema;
189
        $filteredNames = array_filter(preg_grep($regex, glob($globPath, GLOB_BRACE | GLOB_NOESCAPE)),
190
            function (string $fileName) use ($drop) {
191
                return $drop || 0 !== strpos(basename($fileName), 'DropSchema');
192
            });
193
        return $filteredNames;
194
    }
195
    /**
196
     * @var string[] $createDirs
197
     */
198
    private $createDirs;
199
    /**
200
     * @var bool $dropSchema
201
     */
202
    private $dropSchema = false;
203
    /**
204
     * @var string $platform
205
     */
206
    private $platform;
207
}
208