Completed
Push — master ( 178c75...b82d68 )
by Luís
11s
created

DiffCommand   A

Complexity

Total Complexity 22

Size/Duplication

Total Lines 159
Duplicated Lines 0 %

Coupling/Cohesion

Components 2
Dependencies 14

Test Coverage

Coverage 88.61%

Importance

Changes 0
Metric Value
wmc 22
lcom 2
cbo 14
dl 0
loc 159
ccs 70
cts 79
cp 0.8861
rs 10
c 0
b 0
f 0

6 Methods

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