Failed Conditions
Pull Request — master (#617)
by Jonathan
03:01
created

DiffCommand::execute()   C

Complexity

Conditions 9
Paths 9

Size

Total Lines 77
Code Lines 43

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 37
CRAP Score 9.3262

Importance

Changes 0
Metric Value
dl 0
loc 77
ccs 37
cts 44
cp 0.8409
rs 5.7699
c 0
b 0
f 0
cc 9
eloc 43
nc 9
nop 2
crap 9.3262

How to fix   Long Method   

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\DBAL\Migrations\Tools\Console\Command;
6
7
use Doctrine\DBAL\Migrations\Configuration\Configuration;
8
use Doctrine\DBAL\Migrations\Provider\OrmSchemaProvider;
9
use Doctrine\DBAL\Migrations\Provider\SchemaProviderInterface;
10
use Doctrine\DBAL\Version as DbalVersion;
11
use InvalidArgumentException;
12
use SqlFormatter;
13
use Symfony\Component\Console\Input\InputInterface;
14
use Symfony\Component\Console\Input\InputOption;
15
use Symfony\Component\Console\Output\OutputInterface;
16
use function array_unshift;
17
use function class_exists;
18
use function file_get_contents;
19
use function implode;
20
use function preg_match;
21
use function sprintf;
22
use function stripos;
23
use function strlen;
24
use function strpos;
25
use function substr;
26
use function var_export;
27
28
class DiffCommand extends GenerateCommand
29
{
30
    /** @var null|SchemaProviderInterface */
31
    protected $schemaProvider;
32
33 8
    public function __construct(?SchemaProviderInterface $schemaProvider = null)
34
    {
35 8
        $this->schemaProvider = $schemaProvider;
36
37 8
        parent::__construct();
38 8
    }
39
40 8
    protected function configure() : void
41
    {
42 8
        parent::configure();
43
44
        $this
45 8
            ->setName('migrations:diff')
46 8
            ->setDescription('Generate a migration by comparing your current database to your mapping information.')
47 8
            ->setHelp(<<<EOT
48 8
The <info>%command.name%</info> command generates a migration by comparing your current database to your mapping information:
49
50
    <info>%command.full_name%</info>
51
52
You can optionally specify a <comment>--editor-cmd</comment> option to open the generated file in your favorite editor:
53
54
    <info>%command.full_name% --editor-cmd=mate</info>
55
EOT
56
            )
57 8
            ->addOption(
58 8
                'filter-expression',
59 8
                null,
60 8
                InputOption::VALUE_OPTIONAL,
61 8
                'Tables which are filtered by Regular Expression.'
62
            )
63 8
            ->addOption(
64 8
                'formatted',
65 8
                null,
66 8
                InputOption::VALUE_NONE,
67 8
                'Format the generated SQL.'
68
            )
69 8
            ->addOption(
70 8
                'line-length',
71 8
                null,
72 8
                InputOption::VALUE_OPTIONAL,
73 8
                'Max line length of unformatted lines.',
74 8
                120
75
            )
76
        ;
77 8
    }
78
79 8
    public function execute(
80
        InputInterface $input,
81
        OutputInterface $output
82
    ) : void {
83 8
        $isDbalOld     = (DbalVersion::compare('2.2.0') > 0);
84 8
        $configuration = $this->getMigrationConfiguration($input, $output);
85
86 8
        $this->loadCustomTemplate($configuration, $output);
87
88 8
        $conn = $configuration->getConnection();
89
90 8
        $platform = $conn->getDatabasePlatform();
91
92 8
        $filterExpr = $input->getOption('filter-expression');
93
94 8
        if ($filterExpr) {
95
            if ($isDbalOld) {
96
                throw new InvalidArgumentException(
97
                    'The "--filter-expression" option can only be used as of Doctrine DBAL 2.2'
98
                );
99
            }
100
101
            $conn->getConfiguration()
102
                ->setFilterSchemaAssetsExpression($filterExpr);
103
        }
104
105 8
        $fromSchema = $conn->getSchemaManager()->createSchema();
106
107 8
        $toSchema = $this->getSchemaProvider()->createSchema();
108
109 8
        $filterExpr = $conn->getConfiguration()->getFilterSchemaAssetsExpression();
110
111
        // Not using value from options, because filters can be set from config.yml
112 8
        if (! $isDbalOld && $filterExpr) {
113 2
            foreach ($toSchema->getTables() as $table) {
114 2
                $tableName = $table->getName();
115 2
                if (preg_match($filterExpr, $this->resolveTableName($tableName))) {
116 2
                    continue;
117
                }
118
119 1
                $toSchema->dropTable($tableName);
120
            }
121
        }
122
123 8
        $up = $this->buildCodeFromSql(
124 8
            $configuration,
125 8
            $fromSchema->getMigrateToSql($toSchema, $platform),
126 8
            $input->getOption('formatted'),
127 8
            $input->getOption('line-length')
128
        );
129
130 8
        $down = $this->buildCodeFromSql(
131 8
            $configuration,
132 8
            $fromSchema->getMigrateFromSql($toSchema, $platform),
133 8
            $input->getOption('formatted'),
134 8
            $input->getOption('line-length')
135
        );
136
137 8
        if (! $up && ! $down) {
138
            $output->writeln('No changes detected in your mapping information.');
139
140
            return;
141
        }
142
143 8
        $version = $configuration->generateVersionNumber();
144 8
        $path    = $this->generateMigration($configuration, $input, $version, $up, $down);
145
146 8
        $output->writeln(
147 8
            sprintf(
148 8
                'Generated new migration class to "<info>%s</info>" from schema differences.',
149 8
                $path
150
            )
151
        );
152
153 8
        $output->writeln(
154 8
            file_get_contents($path),
155 8
            OutputInterface::VERBOSITY_VERBOSE
156
        );
157 8
    }
158
159
    /** @param string[] $sql */
160 8
    private function buildCodeFromSql(
161
        Configuration $configuration,
162
        array $sql,
163
        bool $formatted = false,
164
        int $lineLength = 120
165
    ) : string {
166 8
        $currentPlatform = $configuration->getConnection()->getDatabasePlatform()->getName();
167 8
        $code            = [];
168
169 8
        foreach ($sql as $query) {
170 8
            if (stripos($query, $configuration->getMigrationsTableName()) !== false) {
171
                continue;
172
            }
173
174 8
            if ($formatted) {
175 1
                if (! class_exists('SqlFormatter')) {
176
                    throw new InvalidArgumentException(
177
                        'The "--formatted" option can only be used if the sql formatter is installed. Please run "composer require jdorn/sql-formatter".'
178
                    );
179
                }
180
181 1
                $maxLength = $lineLength - 18 - 8; // max - php code length - indentation
182
183 1
                if (strlen($query) > $maxLength) {
184 1
                    $query = SqlFormatter::format($query, false);
185
                }
186
            }
187
188 8
            $code[] = sprintf('$this->addSql(%s);', var_export($query, true));
189
        }
190
191 8
        if (! empty($code)) {
192 8
            array_unshift(
193
                $code,
194 8
                sprintf(
195 8
                    '$this->abortIf($this->connection->getDatabasePlatform()->getName() !== %s, %s);',
196 8
                    var_export($currentPlatform, true),
197 8
                    var_export(sprintf("Migration can only be executed safely on '%s'.", $currentPlatform), true)
198
                ),
199 8
                ''
200
            );
201
        }
202
203 8
        return implode("\n", $code);
204
    }
205
206 8
    private function getSchemaProvider() : SchemaProviderInterface
207
    {
208 8
        if (! $this->schemaProvider) {
209 1
            $this->schemaProvider = new OrmSchemaProvider(
210 1
                $this->getHelper('entityManager')->getEntityManager()
211
            );
212
        }
213
214 8
        return $this->schemaProvider;
215
    }
216
217
    /**
218
     * Resolve a table name from its fully qualified name. The `$name` argument
219
     * comes from Doctrine\DBAL\Schema\Table#getName which can sometimes return
220
     * a namespaced name with the form `{namespace}.{tableName}`. This extracts
221
     * the table name from that.
222
     */
223 2
    private function resolveTableName(string $name) : string
224
    {
225 2
        $pos = strpos($name, '.');
226
227 2
        return $pos === false ? $name : substr($name, $pos + 1);
228
    }
229
}
230