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\ExecutedMigrationsList; |
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
|
|
|
|
35
|
3 |
View Code Duplication |
protected function configure() : void |
36
|
|
|
{ |
37
|
3 |
|
parent::configure(); |
38
|
|
|
|
39
|
|
|
$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
|
3 |
|
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
|
|
|
) |
49
|
3 |
|
->addOption( |
50
|
3 |
|
'namespace', |
51
|
3 |
|
null, |
52
|
3 |
|
InputOption::VALUE_REQUIRED, |
53
|
3 |
|
'The namespace to use for the migration (must be in the list of configured namespaces)' |
54
|
|
|
) |
55
|
3 |
|
->addOption( |
56
|
3 |
|
'filter-expression', |
57
|
3 |
|
null, |
58
|
3 |
|
InputOption::VALUE_REQUIRED, |
59
|
3 |
|
'Tables which are filtered by Regular Expression.' |
60
|
|
|
) |
61
|
3 |
|
->addOption( |
62
|
3 |
|
'formatted', |
63
|
3 |
|
null, |
64
|
3 |
|
InputOption::VALUE_NONE, |
65
|
3 |
|
'Format the generated SQL.' |
66
|
|
|
) |
67
|
3 |
|
->addOption( |
68
|
3 |
|
'line-length', |
69
|
3 |
|
null, |
70
|
3 |
|
InputOption::VALUE_REQUIRED, |
71
|
3 |
|
'Max line length of unformatted lines.', |
72
|
3 |
|
120 |
73
|
|
|
) |
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
|
3 |
|
false |
80
|
|
|
) |
81
|
3 |
|
->addOption( |
82
|
3 |
|
'allow-empty-diff', |
83
|
3 |
|
null, |
84
|
3 |
|
InputOption::VALUE_NONE, |
85
|
3 |
|
'Do not throw an exception when no changes are detected.' |
86
|
|
|
); |
87
|
3 |
|
} |
88
|
|
|
|
89
|
|
|
/** |
90
|
|
|
* @throws InvalidOptionUsage |
91
|
|
|
*/ |
92
|
3 |
|
protected function execute( |
93
|
|
|
InputInterface $input, |
94
|
|
|
OutputInterface $output |
95
|
|
|
) : int { |
96
|
3 |
|
$filterExpression = (string) $input->getOption('filter-expression'); |
97
|
3 |
|
if ($filterExpression === '') { |
98
|
2 |
|
$filterExpression = null; |
99
|
|
|
} |
100
|
|
|
|
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
|
3 |
|
if ($namespace === '') { |
107
|
|
|
$namespace = null; |
108
|
|
|
} |
109
|
|
|
|
110
|
3 |
|
if ($formatted) { |
111
|
1 |
|
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
|
|
|
|
118
|
3 |
|
$configuration = $this->getDependencyFactory()->getConfiguration(); |
119
|
|
|
|
120
|
3 |
|
$dirs = $configuration->getMigrationDirectories(); |
121
|
3 |
View Code Duplication |
if ($namespace === null) { |
|
|
|
|
122
|
2 |
|
$namespace = key($dirs); |
123
|
1 |
|
} elseif (! isset($dirs[$namespace])) { |
124
|
|
|
throw new OutOfBoundsException(sprintf('Path not defined for the namespace %s', $namespace)); |
125
|
|
|
} |
126
|
|
|
|
127
|
3 |
|
assert(is_string($namespace)); |
128
|
|
|
|
129
|
3 |
|
$statusCalculator = $this->getDependencyFactory()->getMigrationStatusCalculator(); |
130
|
3 |
|
$executedUnavailableMigrations = $statusCalculator->getExecutedUnavailableMigrations(); |
131
|
3 |
|
$newMigrations = $statusCalculator->getNewMigrations(); |
132
|
|
|
|
133
|
3 |
|
if (! $this->checkNewMigrationsOrExecutedUnavailable($newMigrations, $executedUnavailableMigrations, $input, $output)) { |
134
|
2 |
|
$this->io->error('Migration cancelled!'); |
135
|
|
|
|
136
|
2 |
|
return 3; |
137
|
|
|
} |
138
|
|
|
|
139
|
1 |
|
$fqcn = $this->getDependencyFactory()->getClassNameGenerator()->generateClassName($namespace); |
140
|
|
|
|
141
|
1 |
|
$diffGenerator = $this->getDependencyFactory()->getDiffGenerator(); |
142
|
|
|
|
143
|
|
|
try { |
144
|
1 |
|
$path = $diffGenerator->generate( |
145
|
1 |
|
$fqcn, |
146
|
1 |
|
$filterExpression, |
147
|
1 |
|
$formatted, |
148
|
1 |
|
$lineLength, |
149
|
1 |
|
$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
|
|
|
|
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
|
1 |
|
addslashes($fqcn) |
167
|
|
|
), |
168
|
1 |
|
'', |
169
|
1 |
|
sprintf( |
170
|
1 |
|
'To revert the migration you can use <info>migrations:execute --down \'%s\'</info>', |
171
|
1 |
|
addslashes($fqcn) |
172
|
|
|
), |
173
|
1 |
|
'', |
174
|
|
|
]); |
175
|
|
|
|
176
|
1 |
|
return 0; |
177
|
|
|
} |
178
|
|
|
|
179
|
3 |
|
private function checkNewMigrationsOrExecutedUnavailable( |
180
|
|
|
AvailableMigrationsList $newMigrations, |
181
|
|
|
ExecutedMigrationsList $executedUnavailableMigrations, |
182
|
|
|
InputInterface $input, |
183
|
|
|
OutputInterface $output |
|
|
|
|
184
|
|
|
) : bool { |
185
|
3 |
|
if (count($newMigrations) === 0 && count($executedUnavailableMigrations) === 0) { |
186
|
1 |
|
return true; |
187
|
|
|
} |
188
|
|
|
|
189
|
2 |
|
if (count($newMigrations) !== 0) { |
190
|
2 |
|
$this->io->warning(sprintf( |
191
|
2 |
|
'You have %d available migrations to execute.', |
192
|
2 |
|
count($newMigrations) |
193
|
|
|
)); |
194
|
|
|
} |
195
|
|
|
|
196
|
2 |
|
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
|
1 |
|
count($executedUnavailableMigrations) |
200
|
|
|
)); |
201
|
|
|
} |
202
|
|
|
|
203
|
2 |
|
return $this->canExecute('Are you sure you wish to continue?', $input); |
204
|
|
|
} |
205
|
|
|
} |
206
|
|
|
|
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.