Failed Conditions
Pull Request — master (#968)
by Edi
02:04
created

DiffCommand   A

Complexity

Total Complexity 16

Size/Duplication

Total Lines 174
Duplicated Lines 0 %

Test Coverage

Coverage 78.85%

Importance

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

3 Methods

Rating   Name   Duplication   Size   Complexity  
A configure() 0 51 1
A checkNewMigrationsOrExecutedUnavailable() 0 25 5
B execute() 0 85 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 1
    protected function configure() : void
35
    {
36 1
        parent::configure();
37
38
        $this
39 1
            ->setAliases(['diff'])
40 1
            ->setDescription('Generate a migration by comparing your current database to your mapping information.')
41 1
            ->setHelp(<<<EOT
42 1
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 1
            ->addOption(
49 1
                'namespace',
50 1
                null,
51 1
                InputOption::VALUE_REQUIRED,
52 1
                'The namespace to use for the migration (must be in the list of configured namespaces)'
53
            )
54 1
            ->addOption(
55 1
                'filter-expression',
56 1
                null,
57 1
                InputOption::VALUE_REQUIRED,
58 1
                'Tables which are filtered by Regular Expression.'
59
            )
60 1
            ->addOption(
61 1
                'formatted',
62 1
                null,
63 1
                InputOption::VALUE_NONE,
64 1
                'Format the generated SQL.'
65
            )
66 1
            ->addOption(
67 1
                'line-length',
68 1
                null,
69 1
                InputOption::VALUE_REQUIRED,
70 1
                'Max line length of unformatted lines.',
71 1
                120
72
            )
73 1
            ->addOption(
74 1
                'check-database-platform',
75 1
                null,
76 1
                InputOption::VALUE_OPTIONAL,
77 1
                'Check Database Platform to the generated code.',
78 1
                false
79
            )
80 1
            ->addOption(
81 1
                'allow-empty-diff',
82 1
                null,
83 1
                InputOption::VALUE_NONE,
84 1
                'Do not throw an exception when no changes are detected.'
85
            );
86 1
    }
87
88
    /**
89
     * @throws InvalidOptionUsage
90
     */
91 1
    protected function execute(
92
        InputInterface $input,
93
        OutputInterface $output
94
    ) : int {
95 1
        $filterExpression = (string) $input->getOption('filter-expression');
96 1
        if ($filterExpression === '') {
97
            $filterExpression = null;
98
        }
99
100 1
        $formatted       = filter_var($input->getOption('formatted'), FILTER_VALIDATE_BOOLEAN);
101 1
        $lineLength      = (int) $input->getOption('line-length');
102 1
        $allowEmptyDiff  = $input->getOption('allow-empty-diff');
103 1
        $checkDbPlatform = filter_var($input->getOption('check-database-platform'), FILTER_VALIDATE_BOOLEAN);
104 1
        $namespace       = $input->getOption('namespace');
105 1
        if ($namespace === '') {
106
            $namespace = null;
107
        }
108
109 1
        if ($formatted) {
110 1
            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 1
        $configuration = $this->getDependencyFactory()->getConfiguration();
118
119 1
        $dirs = $configuration->getMigrationDirectories();
120 1
        if ($namespace === null) {
121
            $namespace = key($dirs);
122 1
        } 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 1
        assert(is_string($namespace));
127
128 1
        $statusCalculator              = $this->getDependencyFactory()->getMigrationStatusCalculator();
129 1
        $executedUnavailableMigrations = $statusCalculator->getExecutedUnavailableMigrations();
130 1
        $newMigrations                 = $statusCalculator->getNewMigrations();
131
132 1
        if (! $this->checkNewMigrationsOrExecutedUnavailable($newMigrations, $executedUnavailableMigrations, $input, $output)) {
133
            $this->io->error('Migration cancelled!');
134
135
            return 3;
136
        }
137
138 1
        $fqcn = $this->getDependencyFactory()->getClassNameGenerator()->generateClassName($namespace);
139
140 1
        $diffGenerator = $this->getDependencyFactory()->getDiffGenerator();
141
142
        try {
143 1
            $path = $diffGenerator->generate(
144 1
                $fqcn,
145 1
                $filterExpression,
146 1
                $formatted,
147 1
                $lineLength,
148 1
                $checkDbPlatform
149
            );
150
        } catch (NoChangesDetected $exception) {
151
            if ($allowEmptyDiff) {
152
                $this->io->error($exception->getMessage());
153
154
                return 0;
155
            }
156
157
            throw $exception;
158
        }
159
160 1
        $this->io->text([
161 1
            sprintf('Generated new migration class to "<info>%s</info>"', $path),
162 1
            '',
163 1
            sprintf(
164 1
                'To run just this migration for testing purposes, you can use <info>migrations:execute --up \'%s\'</info>',
165 1
                addslashes($fqcn)
166
            ),
167 1
            '',
168 1
            sprintf(
169 1
                'To revert the migration you can use <info>migrations:execute --down \'%s\'</info>',
170 1
                addslashes($fqcn)
171
            ),
172 1
            '',
173
        ]);
174
175 1
        return 0;
176
    }
177
178 1
    private function checkNewMigrationsOrExecutedUnavailable(
179
        AvailableMigrationsList $newMigrations,
180
        ExecutedMigrationsSet $executedUnavailableMigrations,
181
        InputInterface $input,
182
        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

182
        /** @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...
183
    ) : bool {
184 1
        if (count($newMigrations) === 0 && count($executedUnavailableMigrations) === 0) {
185 1
            return true;
186
        }
187
188
        if (count($newMigrations) !== 0) {
189
            $this->io->error(sprintf(
190
                'WARNING! You have %d available migrations to execute.',
191
                count($newMigrations)
192
            ));
193
        }
194
195
        if (count($executedUnavailableMigrations) !== 0) {
196
            $this->io->error(sprintf(
197
                'WARNING! You have %d previously executed migrations in the database that are not registered migrations.',
198
                count($executedUnavailableMigrations)
199
            ));
200
        }
201
202
        return $this->canExecute('Are you sure you wish to continue?', $input);
203
    }
204
}
205