Completed
Push — master ( b10d2b...6542c0 )
by Andreas
03:51
created

schema   A

Complexity

Total Complexity 33

Size/Duplication

Total Lines 169
Duplicated Lines 0 %

Test Coverage

Coverage 0%

Importance

Changes 8
Bugs 0 Features 0
Metric Value
eloc 106
c 8
b 0
f 0
dl 0
loc 169
ccs 0
cts 105
cp 0
rs 9.76
wmc 33

4 Methods

Rating   Name   Duplication   Size   Complexity  
A generate_proxyfiles() 0 12 3
F execute() 0 77 17
C process_updates() 0 62 12
A configure() 0 7 1
1
<?php
2
/**
3
 * @author CONTENT CONTROL http://www.contentcontrol-berlin.de/
4
 * @copyright CONTENT CONTROL http://www.contentcontrol-berlin.de/
5
 * @license http://www.gnu.org/licenses/lgpl.html GNU Lesser General Public License
6
 */
7
8
namespace midgard\portable\command;
9
10
use midgard\portable\storage\connection;
11
use midgard\portable\classgenerator;
12
use Symfony\Component\Console\Command\Command;
13
use Symfony\Component\Console\Input\InputInterface;
14
use Symfony\Component\Console\Input\InputArgument;
15
use Symfony\Component\Console\Output\OutputInterface;
16
use Symfony\Component\Console\Helper\ProgressBar;
17
use Symfony\Component\Console\Question\Question;
18
use midgard_storage;
19
use midgard_connection;
20
use Doctrine\ORM\Tools\SchemaTool;
21
use Doctrine\DBAL\Schema\Comparator;
22
use Symfony\Component\Console\Input\InputOption;
23
use Doctrine\Common\Proxy\ProxyGenerator;
24
use Doctrine\DBAL\Schema\Column;
25
26
/**
27
 * (Re)generate mapping information from MgdSchema XMLs
28
 */
29
class schema extends Command
30
{
31
    public $connected = false;
32
33
    protected function configure()
34
    {
35
        $this->setName('schema')
36
            ->setDescription('(Re)generate mapping information from MgdSchema XMLs')
37
            ->addArgument('config', InputArgument::OPTIONAL, 'Full path to midgard-portable config file')
38
            ->addOption('force', null, InputOption::VALUE_NONE, 'Ignore errors from DB')
39
            ->addOption('delete', null, InputOption::VALUE_NONE, 'Delete columns/tables that are not defined in mgdschema');
40
    }
41
42
    protected function execute(InputInterface $input, OutputInterface $output) : int
43
    {
44
        if (!$this->connected) {
45
            $path = $input->getArgument('config');
46
            if (empty($path)) {
47
                if (file_exists(OPENPSA_PROJECT_BASEDIR . 'config/midgard-portable.inc.php')) {
0 ignored issues
show
Bug introduced by
The constant midgard\portable\command\OPENPSA_PROJECT_BASEDIR was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
48
                    $path = OPENPSA_PROJECT_BASEDIR . 'config/midgard-portable.inc.php';
49
                } else {
50
                    $dialog = $this->getHelper('question');
51
                    $path = $dialog->ask($input, $output, new Question('<question>Enter path to config file</question>'));
52
                }
53
            }
54
            if (!file_exists($path)) {
55
                throw new \RuntimeException('Config file ' . $path . ' not found');
56
            }
57
            //we have to delay startup so that we can delete the entity class file before it gets included
58
            connection::set_autostart(false);
59
            require $path;
60
        }
61
62
        $mgd_config = midgard_connection::get_instance()->config;
63
        $mgdschema_file = $mgd_config->vardir . '/mgdschema_classes.php';
64
        if (   file_exists($mgdschema_file)
65
            && !unlink($mgdschema_file)) {
66
            throw new \RuntimeException('Could not unlink ' . $mgdschema_file);
67
        }
68
        if (connection::get_parameter('dev_mode') !== true) {
69
            $driver = connection::get_parameter('driver');
70
            $classgenerator = new classgenerator($driver->get_manager(), $mgdschema_file);
71
            $classgenerator->write($driver->get_namespace());
72
        }
73
        if (!file_exists($mgd_config->blobdir . '/0/0')) {
74
            $mgd_config->create_blobdir();
75
        }
76
        connection::startup();
77
        $em = connection::get_em();
78
        connection::invalidate_cache();
79
        $cms = $em->getMetadataFactory()->getAllMetadata();
80
81
        // create storage
82
        if (    !midgard_storage::create_base_storage()
83
             && midgard_connection::get_instance()->get_error_string() != 'MGD_ERR_OK') {
84
            throw new \Exception("Failed to create base database structures" . midgard_connection::get_instance()->get_error_string());
85
        }
86
        $force = $input->getOption('force');
87
        $to_update = [];
88
        $to_create = [];
89
90
        foreach ($cms as $cm) {
91
            if ($em->getConnection()->getSchemaManager()->tablesExist([$cm->getTableName()])) {
92
                $to_update[] = $cm;
93
            } else {
94
                $to_create[] = $cm;
95
            }
96
        }
97
98
        if (!empty($to_create)) {
99
            $output->writeln('Creating <info>' . count($to_create) . '</info> new tables');
100
            $tool = new SchemaTool($em);
101
            try {
102
                $tool->createSchema($to_create);
103
            } catch (\Exception $e) {
104
                if (!$force) {
105
                    throw $e;
106
                }
107
                $output->writeln('<error>' . $e->getMessage() . '</error>');
108
            }
109
        }
110
        if (!empty($to_update)) {
111
            $delete = $input->getOption('delete');
112
            $this->process_updates($to_update, $output, $force, $delete);
113
        }
114
        $output->writeln('Generating proxies');
115
        $this->generate_proxyfiles($cms);
116
117
        $output->writeln('Done');
118
        return 0;
119
    }
120
121
    private function generate_proxyfiles(array $cms)
122
    {
123
        $em = connection::get_em();
124
        $generator = new ProxyGenerator($em->getConfiguration()->getProxyDir(), $em->getConfiguration()->getProxyNamespace());
0 ignored issues
show
Deprecated Code introduced by
The function Doctrine\ORM\Configuration::getProxyNamespace() has been deprecated: 2.7 We're switch to `ocramius/proxy-manager` and this method isn't applicable any longer ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

124
        $generator = new ProxyGenerator($em->getConfiguration()->getProxyDir(), /** @scrutinizer ignore-deprecated */ $em->getConfiguration()->getProxyNamespace());

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
Deprecated Code introduced by
The function Doctrine\ORM\Configuration::getProxyDir() has been deprecated: 2.7 We're switch to `ocramius/proxy-manager` and this method isn't applicable any longer ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

124
        $generator = new ProxyGenerator(/** @scrutinizer ignore-deprecated */ $em->getConfiguration()->getProxyDir(), $em->getConfiguration()->getProxyNamespace());

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
125
        $generator->setPlaceholder('baseProxyInterface', 'Doctrine\ORM\Proxy\Proxy');
126
127
        foreach ($cms as $cm) {
128
            $filename = $generator->getProxyFileName($cm->getName());
129
            if (file_exists($filename)) {
130
                unlink($filename);
131
            }
132
            $generator->generateProxyClass($cm, $filename);
133
        }
134
    }
135
136
    private function process_updates(array $to_update, OutputInterface $output, $force, $delete)
137
    {
138
        $em = connection::get_em();
139
        $conn = $em->getConnection();
140
        $tool = new SchemaTool($em);
141
        $from = $conn->getSchemaManager()->createSchema();
142
        $to = $tool->getSchemaFromMetadata($to_update);
143
144
        $diff = (new Comparator)->compare($from, $to);
145
146
        /*
147
         * Since we normally don't delete old columns, we have to disable DBAL's renaming
148
         * detection, because otherwise a new column might just reuse an outdated one (keeping the values)
149
         */
150
        foreach ($diff->changedTables as $changed_table) {
151
            if (!empty($changed_table->renamedColumns)) {
152
                if (empty($changed_table->addedColumns)) {
153
                    $changed_table->addedColumns = [];
154
                }
155
156
                foreach ($changed_table->renamedColumns as $name => $column) {
157
                    $changed_table->addedColumns[$column->getName()] = $column;
158
                    $changed_table->removedColumns[$name] = new Column($name, $column->getType());
159
                }
160
                $changed_table->renamedColumns = [];
161
            }
162
            if (!$delete) {
163
                $changed_table->removedColumns = [];
164
            }
165
        }
166
167
        if ($delete) {
168
            $sql = $diff->toSql($conn->getDatabasePlatform());
169
        } else {
170
            $sql = $diff->toSaveSql($conn->getDatabasePlatform());
171
        }
172
173
        if (empty($sql)) {
174
            return;
175
        }
176
177
        $output->writeln('Executing <info>' . count($sql) . '</info> updates');
178
        $progress = new ProgressBar($output);
179
        $progress->start(count($sql));
180
181
        foreach ($sql as $sql_line) {
182
            if ($output->getVerbosity() >= OutputInterface::VERBOSITY_VERBOSE) {
183
                $output->writeln(' Executing <info>' . $sql_line . '</info>');
184
            }
185
            try {
186
                $conn->executeQuery($sql_line);
187
            } catch (\Exception $e) {
188
                if (!$force) {
189
                    throw $e;
190
                }
191
                $output->writeln('<error>' . $e->getMessage() . '</error>');
192
            }
193
194
            $progress->advance();
195
        }
196
        $progress->finish();
197
        $output->writeln('');
198
    }
199
}
200