Completed
Push — master ( 80642d...9b2b45 )
by Mike
08:37 queued 02:24
created

DiffCommand::configure()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 22
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 12
CRAP Score 1

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 22
ccs 12
cts 12
cp 1
rs 9.2
cc 1
eloc 11
nc 1
nop 0
crap 1
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 5
    public function __construct(SchemaProviderInterface $schemaProvider=null)
47
    {
48 5
        $this->schemaProvider = $schemaProvider;
49 5
        parent::__construct();
50 5
    }
51
52 5
    protected function configure()
53
    {
54 5
        parent::configure();
55
56 5
        $this
57 5
            ->setName('migrations:diff')
58 5
            ->setDescription('Generate a migration by comparing your current database to your mapping information.')
59 5
            ->setHelp(<<<EOT
60 1
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 5
            )
69 5
            ->addOption('filter-expression', null, InputOption::VALUE_OPTIONAL, 'Tables which are filtered by Regular Expression.')
70 5
            ->addOption('formatted', null, InputOption::VALUE_NONE, 'Format the generated SQL.')
71 5
            ->addOption('line-length', null, InputOption::VALUE_OPTIONAL, 'Max line length of unformatted lines.', 120)
72
        ;
73 5
    }
74
75 5
    public function execute(InputInterface $input, OutputInterface $output)
76
    {
77 5
        $isDbalOld = (DbalVersion::compare('2.2.0') > 0);
78 5
        $configuration = $this->getMigrationConfiguration($input, $output);
79
80 5
        $conn = $configuration->getConnection();
81 5
        $platform = $conn->getDatabasePlatform();
82
83 5
        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 5
        $fromSchema = $conn->getSchemaManager()->createSchema();
93 5
        $toSchema = $this->getSchemaProvider()->createSchema();
94
95
        //Not using value from options, because filters can be set from config.yml
96 5
        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 1
                    $toSchema->dropTable($tableName);
101 1
                }
102 2
            }
103 2
        }
104
105 5
        $up = $this->buildCodeFromSql(
106 5
            $configuration,
107 5
            $fromSchema->getMigrateToSql($toSchema, $platform),
108 5
            $input->getOption('formatted'),
109 5
            $input->getOption('line-length')
110 5
        );
111 5
        $down = $this->buildCodeFromSql(
112 5
            $configuration,
113 5
            $fromSchema->getMigrateFromSql($toSchema, $platform),
114 5
            $input->getOption('formatted'),
115 5
            $input->getOption('line-length')
116 5
        );
117
118 5
        if (! $up && ! $down) {
119
            $output->writeln('No changes detected in your mapping information.', 'ERROR');
120
121
            return;
122
        }
123
124 5
        $version = date('YmdHis');
125 5
        $path = $this->generateMigration($configuration, $input, $version, $up, $down);
126
127 5
        $output->writeln(sprintf('Generated new migration class to "<info>%s</info>" from schema differences.', $path));
128 5
    }
129
130 5
    private function buildCodeFromSql(Configuration $configuration, array $sql, $formatted=false, $lineLength=0)
131
    {
132 5
        $currentPlatform = $configuration->getConnection()->getDatabasePlatform()->getName();
133 5
        $code = [];
134 5
        foreach ($sql as $query) {
135 5
            if (stripos($query, $configuration->getMigrationsTableName()) !== false) {
136
                continue;
137
            }
138
139 5
            if ($formatted) {
140 1
                if (!class_exists('\SqlFormatter')) {
141
                    throw new \InvalidArgumentException(
142
                        'The "--formatted" option can only be used if the sql formatter is installed.'.
143
                        'Please run "composer require jdorn/sql-formatter".'
144
                    );
145
                }
146
147 1
                $maxLength = $lineLength - 18 - 8; // max - php code length - indentation
148
149 1
                if (strlen($query) > $maxLength) {
150 1
                    $query = \SqlFormatter::format($query, false);
151 1
                }
152 1
            }
153
154 5
            $code[] = sprintf("\$this->addSql(%s);", var_export($query, true));
155 5
        }
156
157 5
        if (!empty($code)) {
158 5
            array_unshift(
159 5
                $code,
160 5
                sprintf(
161 5
                    "\$this->abortIf(\$this->connection->getDatabasePlatform()->getName() != %s, %s);",
162 5
                    var_export($currentPlatform, true),
163 5
                    var_export(sprintf("Migration can only be executed safely on '%s'.", $currentPlatform), true)
164 5
                ),
165
                ""
166 5
            );
167 5
        }
168
169 5
        return implode("\n", $code);
170
    }
171
172 5
    private function getSchemaProvider()
173
    {
174 5
        if (!$this->schemaProvider) {
175 1
            $this->schemaProvider = new OrmSchemaProvider($this->getHelper('entityManager')->getEntityManager());
176 1
        }
177
178 5
        return $this->schemaProvider;
179
    }
180
181
    /**
182
     * Resolve a table name from its fully qualified name. The `$name` argument
183
     * comes from Doctrine\DBAL\Schema\Table#getName which can sometimes return
184
     * a namespaced name with the form `{namespace}.{tableName}`. This extracts
185
     * the table name from that.
186
     *
187
     * @param   string $name
188
     * @return  string
189
     */
190 2
    private function resolveTableName($name)
191
    {
192 2
        $pos = strpos($name, '.');
193
194 2
        return false === $pos ? $name : substr($name, $pos + 1);
195
    }
196
}
197