Completed
Pull Request — master (#971)
by Grégoire
03:53
created

DiffCommand   A

Complexity

Total Complexity 16

Size/Duplication

Total Lines 173
Duplicated Lines 0 %

Test Coverage

Coverage 69.9%

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 106
c 1
b 0
f 0
dl 0
loc 173
ccs 72
cts 103
cp 0.699
rs 10
wmc 16

3 Methods

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

123
            throw new OutOfBoundsException(sprintf('Path not defined for the namespace %s', /** @scrutinizer ignore-type */ $namespace));
Loading history...
124
        }
125
126 2
        assert(is_string($namespace));
127
128 2
        $statusCalculator              = $this->getDependencyFactory()->getMigrationStatusCalculator();
129 2
        $executedUnavailableMigrations = $statusCalculator->getExecutedUnavailableMigrations();
130 2
        $newMigrations                 = $statusCalculator->getNewMigrations();
131
132 2
        if (! $this->checkNewMigrationsOrExecutedUnavailable($newMigrations, $executedUnavailableMigrations, $input, $output)) {
133 2
            $output->writeln('<error>Migration cancelled!</error>');
134
135 2
            return 3;
136
        }
137
138
        $fqcn = $this->getDependencyFactory()->getClassNameGenerator()->generateClassName($namespace);
139
140
        $diffGenerator = $this->getDependencyFactory()->getDiffGenerator();
141
142
        try {
143
            $path = $diffGenerator->generate(
144
                $fqcn,
145
                $filterExpression,
146
                $formatted,
147
                $lineLength,
148
                $checkDbPlatform
149
            );
150
        } catch (NoChangesDetected $exception) {
151
            if ($allowEmptyDiff) {
152
                $output->writeln($exception->getMessage());
153
154
                return 0;
155
            }
156
157
            throw $exception;
158
        }
159
160
        $output->writeln([
161
            sprintf('Generated new migration class to "<info>%s</info>"', $path),
162
            '',
163
            sprintf(
164
                'To run just this migration for testing purposes, you can use <info>migrations:execute --up \'%s\'</info>',
165
                addslashes($fqcn)
166
            ),
167
            '',
168
            sprintf(
169
                'To revert the migration you can use <info>migrations:execute --down \'%s\'</info>',
170
                addslashes($fqcn)
171
            ),
172
        ]);
173
174
        return 0;
175
    }
176
177 2
    private function checkNewMigrationsOrExecutedUnavailable(
178
        AvailableMigrationsList $newMigrations,
179
        ExecutedMigrationsSet $executedUnavailableMigrations,
180
        InputInterface $input,
181
        OutputInterface $output
182
    ) : bool {
183 2
        if (count($newMigrations) === 0 && count($executedUnavailableMigrations) === 0) {
184
            return true;
185
        }
186
187 2
        if (count($newMigrations) !== 0) {
188 2
            $output->writeln(sprintf(
189 2
                '<error>WARNING! You have %d available migrations to execute.</error>',
190 2
                count($newMigrations)
191
            ));
192
        }
193
194 2
        if (count($executedUnavailableMigrations) !== 0) {
195 1
            $output->writeln(sprintf(
196 1
                '<error>WARNING! You have %d previously executed migrations in the database that are not registered migrations.</error>',
197 1
                count($executedUnavailableMigrations)
198
            ));
199
        }
200
201 2
        return $this->canExecute('Are you sure you wish to continue? (y/n)', $input, $output);
202
    }
203
}
204