Completed
Pull Request — master (#973)
by Asmir
02:18
created

DiffCommand   A

Complexity

Total Complexity 16

Size/Duplication

Total Lines 174
Duplicated Lines 0 %

Test Coverage

Coverage 91.35%

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 107
dl 0
loc 174
ccs 95
cts 104
cp 0.9135
rs 10
c 1
b 0
f 0
wmc 16

3 Methods

Rating   Name   Duplication   Size   Complexity  
A checkNewMigrationsOrExecutedUnavailable() 0 25 5
B execute() 0 85 10
A configure() 0 51 1
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Doctrine\Migrations\Tools\Console\Command;
6
7
use Doctrine\Migrations\Generator\Exception\NoChangesDetected;
8
use Doctrine\Migrations\Metadata\AvailableMigrationsList;
9
use Doctrine\Migrations\Metadata\ExecutedMigrationsSet;
10
use Doctrine\Migrations\Tools\Console\Exception\InvalidOptionUsage;
11
use Doctrine\SqlFormatter\SqlFormatter;
12
use OutOfBoundsException;
13
use Symfony\Component\Console\Input\InputInterface;
14
use Symfony\Component\Console\Input\InputOption;
15
use Symfony\Component\Console\Output\OutputInterface;
16
use function addslashes;
17
use function assert;
18
use function class_exists;
19
use function count;
20
use function filter_var;
21
use function is_string;
22
use function key;
23
use function sprintf;
24
use const FILTER_VALIDATE_BOOLEAN;
25
26
/**
27
 * The DiffCommand class is responsible for generating a migration by comparing your current database schema to
28
 * your mapping information.
29
 */
30
final class DiffCommand extends DoctrineCommand
31
{
32
    /** @var string */
33
    protected static $defaultName = 'migrations:diff';
34 3
35
    protected function configure() : void
36 3
    {
37
        parent::configure();
38
39 3
        $this
40 3
            ->setAliases(['diff'])
41 3
            ->setDescription('Generate a migration by comparing your current database to your mapping information.')
42 3
            ->setHelp(<<<EOT
43
The <info>%command.name%</info> command generates a migration by comparing your current database to your mapping information:
44
45
    <info>%command.full_name%</info>
46
47
EOT
48 3
            )
49 3
            ->addOption(
50 3
                'namespace',
51 3
                null,
52 3
                InputOption::VALUE_REQUIRED,
53
                'The namespace to use for the migration (must be in the list of configured namespaces)'
54 3
            )
55 3
            ->addOption(
56 3
                'filter-expression',
57 3
                null,
58 3
                InputOption::VALUE_REQUIRED,
59
                'Tables which are filtered by Regular Expression.'
60 3
            )
61 3
            ->addOption(
62 3
                'formatted',
63 3
                null,
64 3
                InputOption::VALUE_NONE,
65
                'Format the generated SQL.'
66 3
            )
67 3
            ->addOption(
68 3
                'line-length',
69 3
                null,
70 3
                InputOption::VALUE_REQUIRED,
71 3
                'Max line length of unformatted lines.',
72
                120
73 3
            )
74 3
            ->addOption(
75 3
                'check-database-platform',
76 3
                null,
77 3
                InputOption::VALUE_OPTIONAL,
78 3
                'Check Database Platform to the generated code.',
79
                false
80 3
            )
81 3
            ->addOption(
82 3
                'allow-empty-diff',
83 3
                null,
84 3
                InputOption::VALUE_NONE,
85
                'Do not throw an exception when no changes are detected.'
86 3
            );
87
    }
88
89
    /**
90
     * @throws InvalidOptionUsage
91 3
     */
92
    protected function execute(
93
        InputInterface $input,
94
        OutputInterface $output
95 3
    ) : int {
96 3
        $filterExpression = (string) $input->getOption('filter-expression');
97 2
        if ($filterExpression === '') {
98
            $filterExpression = null;
99
        }
100 3
101 3
        $formatted       = filter_var($input->getOption('formatted'), FILTER_VALIDATE_BOOLEAN);
102 3
        $lineLength      = (int) $input->getOption('line-length');
103 3
        $allowEmptyDiff  = $input->getOption('allow-empty-diff');
104 3
        $checkDbPlatform = filter_var($input->getOption('check-database-platform'), FILTER_VALIDATE_BOOLEAN);
105 3
        $namespace       = $input->getOption('namespace');
106
        if ($namespace === '') {
107
            $namespace = null;
108
        }
109 3
110 1
        if ($formatted) {
111
            if (! class_exists(SqlFormatter::class)) {
112
                throw InvalidOptionUsage::new(
113
                    'The "--formatted" option can only be used if the sql formatter is installed. Please run "composer require doctrine/sql-formatter".'
114
                );
115
            }
116
        }
117 3
118
        $configuration = $this->getDependencyFactory()->getConfiguration();
119 3
120 3
        $dirs = $configuration->getMigrationDirectories();
121 2
        if ($namespace === null) {
122 1
            $namespace = key($dirs);
123
        } elseif (! isset($dirs[$namespace])) {
124
            throw new OutOfBoundsException(sprintf('Path not defined for the namespace %s', $namespace));
0 ignored issues
show
Bug introduced by
It seems like $namespace can also be of type string[]; however, parameter $args of sprintf() 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

124
            throw new OutOfBoundsException(sprintf('Path not defined for the namespace %s', /** @scrutinizer ignore-type */ $namespace));
Loading history...
125
        }
126 3
127
        assert(is_string($namespace));
128 3
129 3
        $statusCalculator              = $this->getDependencyFactory()->getMigrationStatusCalculator();
130 3
        $executedUnavailableMigrations = $statusCalculator->getExecutedUnavailableMigrations();
131
        $newMigrations                 = $statusCalculator->getNewMigrations();
132 3
133 2
        if (! $this->checkNewMigrationsOrExecutedUnavailable($newMigrations, $executedUnavailableMigrations, $input, $output)) {
134
            $this->io->error('Migration cancelled!');
135 2
136
            return 3;
137
        }
138 1
139
        $fqcn = $this->getDependencyFactory()->getClassNameGenerator()->generateClassName($namespace);
140 1
141
        $diffGenerator = $this->getDependencyFactory()->getDiffGenerator();
142
143 1
        try {
144 1
            $path = $diffGenerator->generate(
145 1
                $fqcn,
146 1
                $filterExpression,
147 1
                $formatted,
148 1
                $lineLength,
149
                $checkDbPlatform
150
            );
151
        } catch (NoChangesDetected $exception) {
152
            if ($allowEmptyDiff) {
153
                $this->io->error($exception->getMessage());
154
155
                return 0;
156
            }
157
158
            throw $exception;
159
        }
160 1
161 1
        $this->io->text([
162 1
            sprintf('Generated new migration class to "<info>%s</info>"', $path),
163 1
            '',
164 1
            sprintf(
165 1
                'To run just this migration for testing purposes, you can use <info>migrations:execute --up \'%s\'</info>',
166
                addslashes($fqcn)
167 1
            ),
168 1
            '',
169 1
            sprintf(
170 1
                'To revert the migration you can use <info>migrations:execute --down \'%s\'</info>',
171
                addslashes($fqcn)
172 1
            ),
173
            '',
174
        ]);
175 1
176
        return 0;
177
    }
178 3
179
    private function checkNewMigrationsOrExecutedUnavailable(
180
        AvailableMigrationsList $newMigrations,
181
        ExecutedMigrationsSet $executedUnavailableMigrations,
182
        InputInterface $input,
183
        OutputInterface $output
0 ignored issues
show
Unused Code introduced by
The parameter $output is not used and could be removed. ( Ignorable by Annotation )

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

183
        /** @scrutinizer ignore-unused */ OutputInterface $output

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
184 3
    ) : bool {
185 1
        if (count($newMigrations) === 0 && count($executedUnavailableMigrations) === 0) {
186
            return true;
187
        }
188 2
189 2
        if (count($newMigrations) !== 0) {
190 2
            $this->io->warning(sprintf(
191 2
                'You have %d available migrations to execute.',
192
                count($newMigrations)
193
            ));
194
        }
195 2
196 1
        if (count($executedUnavailableMigrations) !== 0) {
197 1
            $this->io->warning(sprintf(
198 1
                'You have %d previously executed migrations in the database that are not registered migrations.',
199
                count($executedUnavailableMigrations)
200
            ));
201
        }
202 2
203
        return $this->canExecute('Are you sure you wish to continue?', $input);
204
    }
205
}
206