Failed Conditions
Push — master ( b92363...10368b )
by Mike
08:33
created

MigrateCommand   A

Complexity

Total Complexity 18

Size/Duplication

Total Lines 178
Duplicated Lines 0 %

Coupling/Cohesion

Components 2
Dependencies 6

Test Coverage

Coverage 97.4%

Importance

Changes 2
Bugs 0 Features 0
Metric Value
wmc 18
lcom 2
cbo 6
dl 0
loc 178
ccs 75
cts 77
cp 0.974
rs 10
c 2
b 0
f 0

5 Methods

Rating   Name   Duplication   Size   Complexity  
A canExecute() 0 8 3
A configure() 0 48 1
C execute() 0 67 8
A createMigration() 0 4 1
B getVersionNameFromAlias() 0 26 5
1
<?php
2
3
namespace Doctrine\DBAL\Migrations\Tools\Console\Command;
4
5
use Doctrine\DBAL\Migrations\Configuration\Configuration;
6
use Doctrine\DBAL\Migrations\Migration;
7
use Symfony\Component\Console\Formatter\OutputFormatter;
8
use Symfony\Component\Console\Input\InputInterface;
9
use Symfony\Component\Console\Output\OutputInterface;
10
use Symfony\Component\Console\Input\InputArgument;
11
use Symfony\Component\Console\Input\InputOption;
12
13
/**
14
 * Command for executing a migration to a specified version or the latest available version.
15
 *
16
 * @license http://www.opensource.org/licenses/lgpl-license.php LGPL
17
 * @link    www.doctrine-project.org
18
 * @since   2.0
19
 * @author  Jonathan Wage <[email protected]>
20
 */
21
class MigrateCommand extends AbstractCommand
22
{
23 18
    protected function configure()
24
    {
25
        $this
26 18
            ->setName('migrations:migrate')
27 18
            ->setDescription('Execute a migration to a specified version or the latest available version.')
28 18
            ->addArgument('version', InputArgument::OPTIONAL, 'The version number (YYYYMMDDHHMMSS) or alias (first, prev, next, latest) to migrate to.', 'latest')
29 18
            ->addOption('write-sql', null, InputOption::VALUE_OPTIONAL, 'The path to output the migration SQL file instead of executing it. Default to current working directory.')
30 18
            ->addOption('dry-run', null, InputOption::VALUE_NONE, 'Execute the migration as a dry run.')
31 18
            ->addOption('query-time', null, InputOption::VALUE_NONE, 'Time all the queries individually.')
32 18
            ->addOption('allow-no-migration', null, InputOption::VALUE_NONE, 'Don\'t throw an exception if no migration is available (CI).')
33 18
            ->setHelp(<<<EOT
34 18
The <info>%command.name%</info> command executes a migration to a specified version or the latest available version:
35
36
    <info>%command.full_name%</info>
37
38
You can optionally manually specify the version you wish to migrate to:
39
40
    <info>%command.full_name% YYYYMMDDHHMMSS</info>
41
42
You can specify the version you wish to migrate to using an alias:
43
44
    <info>%command.full_name% prev</info>
45
    <info>These alias are defined : first, latest, prev, current and next</info>
46
47
You can specify the version you wish to migrate to using an number against the current version:
48
49
    <info>%command.full_name% current+3</info>
50
51
You can also execute the migration as a <comment>--dry-run</comment>:
52
53
    <info>%command.full_name% YYYYMMDDHHMMSS --dry-run</info>
54
55
You can output the would be executed SQL statements to a file with <comment>--write-sql</comment>:
56
57
    <info>%command.full_name% YYYYMMDDHHMMSS --write-sql</info>
58
59
Or you can also execute the migration without a warning message which you need to interact with:
60
61
    <info>%command.full_name% --no-interaction</info>
62
63
You can also time all the different queries if you wanna know which one is taking so long:
64
65
    <info>%command.full_name% --query-time</info>
66
EOT
67
        );
68
69 18
        parent::configure();
70 18
    }
71
72 11
    public function execute(InputInterface $input, OutputInterface $output)
73
    {
74 11
        $configuration = $this->getMigrationConfiguration($input, $output);
75 11
        $migration     = $this->createMigration($configuration);
76
77 11
        $this->outputHeader($configuration, $output);
78
79 11
        $timeAllqueries = $input->getOption('query-time');
80
81 11
        $dryRun = (boolean) $input->getOption('dry-run');
82 11
        $configuration->setIsDryRun($dryRun);
83
84 11
        $executedMigrations  = $configuration->getMigratedVersions();
85 11
        $availableMigrations = $configuration->getAvailableVersions();
86
87 11
        $version = $this->getVersionNameFromAlias($input->getArgument('version'), $output, $configuration);
88 11
        if ($version === false) {
89 3
            return 1;
90
        }
91
92 8
        $executedUnavailableMigrations = array_diff($executedMigrations, $availableMigrations);
93 8
        if ( ! empty($executedUnavailableMigrations)) {
94 1
            $output->writeln(sprintf(
95
                '<error>WARNING! You have %s previously executed migrations'
96 1
                . ' in the database that are not registered migrations.</error>',
97 1
                count($executedUnavailableMigrations)
98
            ));
99
100 1
            foreach ($executedUnavailableMigrations as $executedUnavailableMigration) {
101 1
                $output->writeln(sprintf(
102 1
                    '    <comment>>></comment> %s (<comment>%s</comment>)',
103 1
                    $configuration->getDateTime($executedUnavailableMigration),
104 1
                    $executedUnavailableMigration
105
                ));
106
            }
107
108 1
            $question = 'Are you sure you wish to continue? (y/n)';
109 1
            if ( ! $this->canExecute($question, $input, $output)) {
110 1
                $output->writeln('<error>Migration cancelled!</error>');
111
112 1
                return 1;
113
            }
114
        }
115
116 7
        if ($path = $input->getOption('write-sql')) {
117 2
            $path = is_bool($path) ? getcwd() : $path;
118 2
            $migration->writeSqlFile($path, $version);
119 2
            return 0;
120
        }
121
122 5
        $cancelled = false;
123 5
        $migration->setNoMigrationException($input->getOption('allow-no-migration'));
124 5
        $result = $migration->migrate($version, $dryRun, $timeAllqueries, function () use ($input, $output, &$cancelled) {
0 ignored issues
show
Unused Code introduced by
$result is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
125
            $question    = 'WARNING! You are about to execute a database migration'
126
                . ' that could result in schema changes and data lost.'
127 4
                . ' Are you sure you wish to continue? (y/n)';
128 4
            $canContinue = $this->canExecute($question, $input, $output);
129 4
            $cancelled   = ! $canContinue;
130
131 4
            return $canContinue;
132 5
        });
133
134 5
        if ($cancelled) {
135 1
            $output->writeln('<error>Migration cancelled!</error>');
136 1
            return 1;
137
        }
138 4
    }
139
140
    /**
141
     * Create a new migration instance to execute the migrations.
142
     *
143
     * @param Configuration $configuration The configuration with which the migrations will be executed
144
     * @return Migration a new migration instance
145
     */
146 1
    protected function createMigration(Configuration $configuration)
147
    {
148 1
        return new Migration($configuration);
149
    }
150
151
    /**
152
     * @param string $question
153
     * @param InputInterface $input
154
     * @param OutputInterface $output
155
     * @return bool
156
     */
157 5
    private function canExecute($question, InputInterface $input, OutputInterface $output)
158
    {
159 5
        if ($input->isInteractive() && ! $this->askConfirmation($question, $input, $output)) {
160 2
            return false;
161
        }
162
163 3
        return true;
164
    }
165
166
    /**
167
     * @param string $versionAlias
168
     * @param OutputInterface $output
169
     * @param Configuration $configuration
170
     * @return bool|string
171
     */
172 11
    private function getVersionNameFromAlias($versionAlias, OutputInterface $output, Configuration $configuration)
173
    {
174 11
        $version = $configuration->resolveVersionAlias($versionAlias);
175 11
        if ($version === null) {
176 3
            if ($versionAlias == 'prev') {
177 1
                $output->writeln('<error>Already at first version.</error>');
178 1
                return false;
179
            }
180 2
            if ($versionAlias == 'next') {
181 1
                $output->writeln('<error>Already at latest version.</error>');
182 1
                return false;
183
            }
184 1
            if (substr($versionAlias, 0, 7) == 'current') {
185
                $output->writeln('<error>The delta couldn\'t be reached.</error>');
186
                return false;
187
            }
188
189 1
            $output->writeln(sprintf(
190 1
                '<error>Unknown version: %s</error>',
191 1
                OutputFormatter::escape($versionAlias)
192
            ));
193 1
            return false;
194
        }
195
196 8
        return $version;
197
    }
198
}
199