Completed
Push — master ( 327acb...621aa7 )
by Andreas
16s
created

DiffCommand   A

Complexity

Total Complexity 22

Size/Duplication

Total Lines 161
Duplicated Lines 0 %

Coupling/Cohesion

Components 2
Dependencies 14

Test Coverage

Coverage 88.75%

Importance

Changes 0
Metric Value
wmc 22
lcom 2
cbo 14
dl 0
loc 161
ccs 71
cts 80
cp 0.8875
rs 10
c 0
b 0
f 0

6 Methods

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