Failed Conditions
Pull Request — master (#632)
by Michael
02:44
created

MigrateCommand::execute()   C

Complexity

Conditions 8
Paths 10

Size

Total Lines 84
Code Lines 48

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 49
CRAP Score 8

Importance

Changes 0
Metric Value
cc 8
eloc 48
nc 10
nop 2
dl 0
loc 84
ccs 49
cts 49
cp 1
crap 8
rs 5.8442
c 0
b 0
f 0

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
declare(strict_types=1);
4
5
namespace Doctrine\Migrations\Tools\Console\Command;
6
7
use Doctrine\Migrations\Configuration\Configuration;
8
use Doctrine\Migrations\Migration;
9
use Symfony\Component\Console\Formatter\OutputFormatter;
10
use Symfony\Component\Console\Input\InputArgument;
11
use Symfony\Component\Console\Input\InputInterface;
12
use Symfony\Component\Console\Input\InputOption;
13
use Symfony\Component\Console\Output\OutputInterface;
14
use function array_diff;
15
use function count;
16
use function getcwd;
17
use function is_bool;
18
use function sprintf;
19
use function substr;
20
21
class MigrateCommand extends AbstractCommand
22
{
23 18
    protected function configure() : void
24
    {
25
        $this
26 18
            ->setName('migrations:migrate')
27 18
            ->setDescription(
28 18
                'Execute a migration to a specified version or the latest available version.'
29
            )
30 18
            ->addArgument(
31 18
                'version',
32 18
                InputArgument::OPTIONAL,
33 18
                'The version number (YYYYMMDDHHMMSS) or alias (first, prev, next, latest) to migrate to.',
34 18
                'latest'
35
            )
36 18
            ->addOption(
37 18
                'write-sql',
38 18
                null,
39 18
                InputOption::VALUE_OPTIONAL,
40 18
                'The path to output the migration SQL file instead of executing it. Default to current working directory.'
41
            )
42 18
            ->addOption(
43 18
                'dry-run',
44 18
                null,
45 18
                InputOption::VALUE_NONE,
46 18
                'Execute the migration as a dry run.'
47
            )
48 18
            ->addOption(
49 18
                'query-time',
50 18
                null,
51 18
                InputOption::VALUE_NONE,
52 18
                'Time all the queries individually.'
53
            )
54 18
            ->addOption(
55 18
                'allow-no-migration',
56 18
                null,
57 18
                InputOption::VALUE_NONE,
58 18
                'Don\'t throw an exception if no migration is available (CI).'
59
            )
60 18
            ->setHelp(<<<EOT
61 18
The <info>%command.name%</info> command executes a migration to a specified version or the latest available version:
62
63
    <info>%command.full_name%</info>
64
65
You can optionally manually specify the version you wish to migrate to:
66
67
    <info>%command.full_name% YYYYMMDDHHMMSS</info>
68
69
You can specify the version you wish to migrate to using an alias:
70
71
    <info>%command.full_name% prev</info>
72
    <info>These alias are defined : first, latest, prev, current and next</info>
73
74
You can specify the version you wish to migrate to using an number against the current version:
75
76
    <info>%command.full_name% current+3</info>
77
78
You can also execute the migration as a <comment>--dry-run</comment>:
79
80
    <info>%command.full_name% YYYYMMDDHHMMSS --dry-run</info>
81
82
You can output the would be executed SQL statements to a file with <comment>--write-sql</comment>:
83
84
    <info>%command.full_name% YYYYMMDDHHMMSS --write-sql</info>
85
86
Or you can also execute the migration without a warning message which you need to interact with:
87
88
    <info>%command.full_name% --no-interaction</info>
89
90
You can also time all the different queries if you wanna know which one is taking so long:
91
92
    <info>%command.full_name% --query-time</info>
93
EOT
94
        );
95
96 18
        parent::configure();
97 18
    }
98
99 11
    public function execute(InputInterface $input, OutputInterface $output) : int
100
    {
101 11
        $configuration = $this->getMigrationConfiguration($input, $output);
102 11
        $migration     = $this->createMigration($configuration);
103
104 11
        $this->outputHeader($configuration, $output);
105
106 11
        $timeAllqueries = $input->getOption('query-time');
107
108 11
        $dryRun = (bool) $input->getOption('dry-run');
109 11
        $configuration->setIsDryRun($dryRun);
110
111 11
        $executedMigrations  = $configuration->getMigratedVersions();
112 11
        $availableMigrations = $configuration->getAvailableVersions();
113
114 11
        $version = $this->getVersionNameFromAlias(
115 11
            $input->getArgument('version'),
116 11
            $output,
117 11
            $configuration
118
        );
119
120 11
        if ($version === '') {
121 3
            return 1;
122
        }
123
124 8
        $executedUnavailableMigrations = array_diff($executedMigrations, $availableMigrations);
125
126 8
        if (! empty($executedUnavailableMigrations)) {
127 1
            $output->writeln(sprintf(
128 1
                '<error>WARNING! You have %s previously executed migrations in the database that are not registered migrations.</error>',
129 1
                count($executedUnavailableMigrations)
130
            ));
131
132 1
            foreach ($executedUnavailableMigrations as $executedUnavailableMigration) {
133 1
                $output->writeln(sprintf(
134 1
                    '    <comment>>></comment> %s (<comment>%s</comment>)',
135 1
                    $configuration->getDateTime($executedUnavailableMigration),
136 1
                    $executedUnavailableMigration
137
                ));
138
            }
139
140 1
            $question = 'Are you sure you wish to continue? (y/n)';
141
142 1
            if (! $this->canExecute($question, $input, $output)) {
143 1
                $output->writeln('<error>Migration cancelled!</error>');
144
145 1
                return 1;
146
            }
147
        }
148
149 7
        $path = $input->getOption('write-sql');
150
151 7
        if ($path) {
152 2
            $path = is_bool($path) ? getcwd() : $path;
153 2
            $migration->writeSqlFile($path, $version);
154
155 2
            return 0;
156
        }
157
158 5
        $cancelled = false;
159
160 5
        $migration->setNoMigrationException($input->getOption('allow-no-migration'));
161
162 5
        $result = $migration->migrate(
0 ignored issues
show
Unused Code introduced by
The assignment to $result is dead and can be removed.
Loading history...
163 5
            $version,
164 5
            $dryRun,
165 5
            $timeAllqueries,
166
            function () use ($input, $output, &$cancelled) {
167 4
                $question = 'WARNING! You are about to execute a database migration that could result in schema changes and data loss. Are you sure you wish to continue? (y/n)';
168
169 4
                $canContinue = $this->canExecute($question, $input, $output);
170 4
                $cancelled   = ! $canContinue;
171
172 4
                return $canContinue;
173 5
            }
174
        );
175
176 5
        if ($cancelled) {
177 1
            $output->writeln('<error>Migration cancelled!</error>');
178
179 1
            return 1;
180
        }
181
182 4
        return 0;
183
    }
184
185 1
    protected function createMigration(Configuration $configuration) : Migration
186
    {
187 1
        return new Migration($configuration);
188
    }
189
190 5
    private function canExecute(
191
        string $question,
192
        InputInterface $input,
193
        OutputInterface $output
194
    ) : bool {
195 5
        if ($input->isInteractive() && ! $this->askConfirmation($question, $input, $output)) {
196 2
            return false;
197
        }
198
199 3
        return true;
200
    }
201
202 11
    private function getVersionNameFromAlias(
203
        string $versionAlias,
204
        OutputInterface $output,
205
        Configuration $configuration
206
    ) : string {
207 11
        $version = $configuration->resolveVersionAlias($versionAlias);
208
209 11
        if ($version === null) {
210 3
            if ($versionAlias === 'prev') {
211 1
                $output->writeln('<error>Already at first version.</error>');
212
213 1
                return '';
214
            }
215
216 2
            if ($versionAlias === 'next') {
217 1
                $output->writeln('<error>Already at latest version.</error>');
218
219 1
                return '';
220
            }
221
222 1
            if (substr($versionAlias, 0, 7) === 'current') {
223
                $output->writeln('<error>The delta couldn\'t be reached.</error>');
224
225
                return '';
226
            }
227
228 1
            $output->writeln(sprintf(
229 1
                '<error>Unknown version: %s</error>',
230 1
                OutputFormatter::escape($versionAlias)
231
            ));
232
233 1
            return '';
234
        }
235
236 8
        return $version;
237
    }
238
}
239