Passed
Push — master ( 229462...d74785 )
by Andreas
02:20 queued 14s
created

VersionCommand::mark()   C

Complexity

Conditions 13
Paths 64

Size

Total Lines 69
Code Lines 42

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 38
CRAP Score 13.0028

Importance

Changes 0
Metric Value
cc 13
eloc 42
nc 64
nop 5
dl 0
loc 69
ccs 38
cts 39
cp 0.9744
crap 13.0028
rs 6.6166
c 0
b 0
f 0

How to fix   Long Method    Complexity   

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\Exception\MigrationClassNotFound;
8
use Doctrine\Migrations\Exception\UnknownMigrationVersion;
9
use Doctrine\Migrations\Metadata\ExecutedMigrationsSet;
10
use Doctrine\Migrations\Tools\Console\Exception\InvalidOptionUsage;
11
use Doctrine\Migrations\Tools\Console\Exception\VersionAlreadyExists;
12
use Doctrine\Migrations\Tools\Console\Exception\VersionDoesNotExist;
13
use Doctrine\Migrations\Version\Direction;
14
use Doctrine\Migrations\Version\ExecutionResult;
15
use Doctrine\Migrations\Version\Version;
16
use Symfony\Component\Console\Input\InputArgument;
17
use Symfony\Component\Console\Input\InputInterface;
18
use Symfony\Component\Console\Input\InputOption;
19
use Symfony\Component\Console\Output\OutputInterface;
20
use function sprintf;
21
22
/**
23
 * The VersionCommand class is responsible for manually adding and deleting migration versions from the tracking table.
24
 */
25
class VersionCommand extends DoctrineCommand
26
{
27
    /** @var string */
28
    protected static $defaultName = 'migrations:version';
29
30
    /** @var bool */
31
    private $markMigrated;
32
33 13
    protected function configure() : void
34
    {
35
        $this
36 13
            ->setAliases(['version'])
37 13
            ->setDescription('Manually add and delete migration versions from the version table.')
38 13
            ->addArgument(
39 13
                'version',
40 13
                InputArgument::OPTIONAL,
41 13
                'The version to add or delete.',
42 13
                null
43
            )
44 13
            ->addOption(
45 13
                'add',
46 13
                null,
47 13
                InputOption::VALUE_NONE,
48 13
                'Add the specified version.'
49
            )
50 13
            ->addOption(
51 13
                'delete',
52 13
                null,
53 13
                InputOption::VALUE_NONE,
54 13
                'Delete the specified version.'
55
            )
56 13
            ->addOption(
57 13
                'all',
58 13
                null,
59 13
                InputOption::VALUE_NONE,
60 13
                'Apply to all the versions.'
61
            )
62 13
            ->addOption(
63 13
                'range-from',
64 13
                null,
65 13
                InputOption::VALUE_OPTIONAL,
66 13
                'Apply from specified version.'
67
            )
68 13
            ->addOption(
69 13
                'range-to',
70 13
                null,
71 13
                InputOption::VALUE_OPTIONAL,
72 13
                'Apply to specified version.'
73
            )
74 13
            ->setHelp(<<<EOT
75 13
The <info>%command.name%</info> command allows you to manually add, delete or synchronize migration versions from the version table:
76
77
    <info>%command.full_name% MIGRATION-FQCN --add</info>
78
79
If you want to delete a version you can use the <comment>--delete</comment> option:
80
81
    <info>%command.full_name% MIGRATION-FQCN --delete</info>
82
83
If you want to synchronize by adding or deleting all migration versions available in the version table you can use the <comment>--all</comment> option:
84
85
    <info>%command.full_name% --add --all</info>
86
    <info>%command.full_name% --delete --all</info>
87
88
If you want to synchronize by adding or deleting some range of migration versions available in the version table you can use the <comment>--range-from/--range-to</comment> option:
89
90
    <info>%command.full_name% --add --range-from=MIGRATION-FQCN --range-to=MIGRATION-FQCN</info>
91
    <info>%command.full_name% --delete --range-from=MIGRATION-FQCN --range-to=MIGRATION-FQCN</info>
92
93
You can also execute this command without a warning message which you need to interact with:
94
95
    <info>%command.full_name% --no-interaction</info>
96
EOT
97
            );
98
99 13
        parent::configure();
100 13
    }
101
102
    /**
103
     * @throws InvalidOptionUsage
104
     */
105 13
    public function execute(InputInterface $input, OutputInterface $output) : ?int
106
    {
107 13
        if ($input->getOption('add') === false && $input->getOption('delete') === false) {
108
            throw InvalidOptionUsage::new('You must specify whether you want to --add or --delete the specified version.');
109
        }
110
111 13
        $this->markMigrated = $input->getOption('add');
112
113 13
        if ($input->isInteractive()) {
114
            $question = 'WARNING! You are about to add, delete or synchronize migration versions from the version table that could result in data lost. Are you sure you wish to continue? (y/n)';
115
116
            $confirmation = $this->askConfirmation($question, $input, $output);
117
118
            if ($confirmation) {
119
                $this->markVersions($input, $output);
120
            } else {
121
                $output->writeln('<error>Migration cancelled!</error>');
122
            }
123
        } else {
124 13
            $this->markVersions($input, $output);
125
        }
126
127 7
        return 0;
128
    }
129
130
    /**
131
     * @throws InvalidOptionUsage
132
     */
133 13
    private function markVersions(InputInterface $input, OutputInterface $output) : void
134
    {
135 13
        $affectedVersion = $input->getArgument('version');
136 13
        $allOption       = $input->getOption('all');
137 13
        $rangeFromOption = $input->getOption('range-from');
138 13
        $rangeToOption   = $input->getOption('range-to');
139
140 13
        if ($allOption === true && ($rangeFromOption !== null || $rangeToOption !== null)) {
141 2
            throw InvalidOptionUsage::new(
142 2
                'Options --all and --range-to/--range-from both used. You should use only one of them.'
143
            );
144
        }
145
146 11
        if ($rangeFromOption !== null xor $rangeToOption !== null) {
147 2
            throw InvalidOptionUsage::new(
148 2
                'Options --range-to and --range-from should be used together.'
149
            );
150
        }
151
152 9
        $executedMigrations = $this->getDependencyFactory()->getMetadataStorage()->getExecutedMigrations();
153 9
        $availableVersions  = $this->getDependencyFactory()->getMigrationRepository()->getMigrations();
154 9
        if ($allOption === true) {
155 2
            if ($input->getOption('delete') === true) {
156 1
                foreach ($executedMigrations->getItems() as $availableMigration) {
157 1
                    $this->mark($input, $output, $availableMigration->getVersion(), false, $executedMigrations);
158
                }
159
            }
160 2
            foreach ($availableVersions->getItems() as $availableMigration) {
161 2
                $this->mark($input, $output, $availableMigration->getVersion(), true, $executedMigrations);
162
            }
163 7
        } elseif ($affectedVersion !== null) {
164 5
            $this->mark($input, $output, new Version($affectedVersion), false, $executedMigrations);
0 ignored issues
show
Bug introduced by
It seems like $affectedVersion can also be of type string[]; however, parameter $version of Doctrine\Migrations\Version\Version::__construct() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

164
            $this->mark($input, $output, new Version(/** @scrutinizer ignore-type */ $affectedVersion), false, $executedMigrations);
Loading history...
165 2
        } elseif ($rangeFromOption !== null && $rangeToOption !== null) {
166 2
            $migrate = false;
167 2
            foreach ($availableVersions->getItems() as $availableMigration) {
168 2
                if ((string) $availableMigration->getVersion() === $rangeFromOption) {
169 2
                    $migrate = true;
170
                }
171
172 2
                if ($migrate) {
173 2
                    $this->mark($input, $output, $availableMigration->getVersion(), true, $executedMigrations);
174
                }
175
176 2
                if ((string) $availableMigration->getVersion() === $rangeToOption) {
177 2
                    break;
178
                }
179
            }
180
        } else {
181
            throw InvalidOptionUsage::new('You must specify the version or use the --all argument.');
182
        }
183 7
    }
184
185
    /**
186
     * @throws VersionAlreadyExists
187
     * @throws VersionDoesNotExist
188
     * @throws UnknownMigrationVersion
189
     */
190 9
    private function mark(InputInterface $input, OutputInterface $output, Version $version, bool $all, ExecutedMigrationsSet $executedMigrations) : void
191
    {
192
        try {
193 9
            $availableMigration = $this->getDependencyFactory()->getMigrationRepository()->getMigration($version);
194 2
        } catch (MigrationClassNotFound $e) {
195 2
            $availableMigration = null;
196
        }
197
198 9
        $storage = $this->getDependencyFactory()->getMetadataStorage();
199 9
        if ($availableMigration === null) {
200 2
            if ($input->getOption('delete') === false) {
201
                throw UnknownMigrationVersion::new((string) $version);
202
            }
203
204
            $question =
205
                'WARNING! You are about to delete a migration version from the version table that has no corresponding migration file.' .
206 2
                'Do you want to delete this migration from the migrations table? (y/n)';
207
208 2
            $confirmation = $this->askConfirmation($question, $input, $output);
209
210 2
            if ($confirmation) {
211 2
                $migrationResult = new ExecutionResult($version, Direction::DOWN);
212 2
                $storage->complete($migrationResult);
213 2
                $output->writeln(sprintf(
214 2
                    '<info>%s</info> deleted from the version table.',
215 2
                    (string) $version
216
                ));
217
218 2
                return;
219
            }
220
        }
221
222 8
        $marked = false;
223
224 8
        if ($this->markMigrated && $executedMigrations->hasMigration($version)) {
225 2
            if (! $all) {
226 1
                throw VersionAlreadyExists::new($version);
227
            }
228
229 1
            $marked = true;
230
        }
231
232 7
        if (! $this->markMigrated && ! $executedMigrations->hasMigration($version)) {
233 2
            if (! $all) {
234 1
                throw VersionDoesNotExist::new($version);
235
            }
236
237 1
            $marked = true;
238
        }
239
240 6
        if ($marked === true) {
241 2
            return;
242
        }
243
244 6
        if ($this->markMigrated) {
245 3
            $migrationResult = new ExecutionResult($version, Direction::UP);
246 3
            $storage->complete($migrationResult);
247
248 3
            $output->writeln(sprintf(
249 3
                '<info>%s</info> added to the version table.',
250 3
                (string) $version
251
            ));
252
        } else {
253 3
            $migrationResult = new ExecutionResult($version, Direction::DOWN);
254 3
            $storage->complete($migrationResult);
255
256 3
            $output->writeln(sprintf(
257 3
                '<info>%s</info> deleted from the version table.',
258 3
                (string) $version
259
            ));
260
        }
261 6
    }
262
}
263