MigrationCommand::execute()   F
last analyzed

Complexity

Conditions 37
Paths 283

Size

Total Lines 169
Code Lines 98

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 61
CRAP Score 73.5578

Importance

Changes 0
Metric Value
cc 37
eloc 98
nc 283
nop 2
dl 0
loc 169
ccs 61
cts 87
cp 0.701
crap 73.5578
rs 2.2958
c 0
b 0
f 0

How to fix   Long Method    Complexity   

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
namespace Kaliop\eZMigrationBundle\Command;
4
5
use Symfony\Component\Console\Input\InputInterface;
6
use Symfony\Component\Console\Output\OutputInterface;
7
use Symfony\Component\Console\Input\InputOption;
8
use Symfony\Component\Console\Input\InputArgument;
9
use Kaliop\eZMigrationBundle\API\Value\Migration;
10
use Kaliop\eZMigrationBundle\API\Value\MigrationDefinition;
11
use Symfony\Component\Console\Question\ConfirmationQuestion;
0 ignored issues
show
Bug introduced by
The type Symfony\Component\Consol...on\ConfirmationQuestion was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
12
13
/**
14
 * Command to manipulate the available migrations / migration definitions.
15
 * @todo should we split off the actions used to manipulate migration defs into a separate command `MigrationDefinition` ?
16
 */
17
class MigrationCommand extends AbstractCommand
18
{
19
    /**
20
     * Set up the command.
21
     *
22
     * Define the name, options and help text.
23
     */
24 148
    protected function configure()
25
    {
26 148
        parent::configure();
27
28
        $this
29 148
            ->setName('kaliop:migration:migration')
30 148
            ->setDescription('Manually modify or get info about migrations in the database table.')
31 148
            ->addOption('delete', null, InputOption::VALUE_NONE, "Delete the specified migration.")
32 148
            ->addOption('info', null, InputOption::VALUE_NONE, "Get info about the specified migration.")
33 148
            ->addOption('add', null, InputOption::VALUE_NONE, "Add the specified migration definition.")
34 148
            ->addOption('skip', null, InputOption::VALUE_NONE, "Mark the specified migration as skipped.")
35 148
            ->addOption('fail', null, InputOption::VALUE_NONE, "Mark the specified migration as failed.")
36 148
            ->addOption('no-interaction', 'n', InputOption::VALUE_NONE, "Do not ask any interactive question.")
37 148
            ->addArgument('migration', InputArgument::REQUIRED, 'The migration to add/skip (filename with full path) or detail/delete (plain migration name).', null)
38 148
            ->setHelp(<<<EOT
39 148
The <info>kaliop:migration:migration</info> command allows you to manually manage migrations.
40
41
To see detailed information about a migration or migration definition:
42
43
    <info>php bin/console kaliop:migration:migration --info migration_name</info>
44
45
    <info>php bin/console kaliop:migration:migration --info /path/to/migration_definition.yml</info>
46
47
To remove a migration from the migration table, or mark it as failed:
48
49
    <info>php bin/console kaliop:migration:migration --delete migration_name</info>
50
51
    <info>php bin/console kaliop:migration:migration --fail migration_name</info>
52
53
To manually add migration definitions to the migration table, or marking them as skipped:
54
55
    <info>php bin/console kaliop:migration:migration --add /path/to/migration_definition</info>
56
57
    <info>php bin/console kaliop:migration:migration --skip /path/to/migration_definition</info>
58
EOT
59
            );
60 148
    }
61
62
    /**
63
     * Execute the command.
64
     *
65
     * @param InputInterface $input
66
     * @param OutputInterface $output
67
     * @return null|int null or 0 if everything went fine, or an error code
68
     */
69 107
    protected function execute(InputInterface $input, OutputInterface $output)
70
    {
71 107
        $this->setOutput($output);
72 107
        $this->setVerbosity($output->getVerbosity());
73
74 107
        if (!$input->getOption('add') && !$input->getOption('delete') && !$input->getOption('skip') &&
75 107
            !$input->getOption('info') && !$input->getOption('fail')) {
76
            throw new \InvalidArgumentException('You must specify whether you want to --add, --delete, --skip, --fail or --info the specified migration.');
77
        }
78
79 107
        $migrationService = $this->getMigrationService();
80 107
        $migrationNameOrPath = $input->getArgument('migration');
81
82 107
        if ($input->getOption('info')) {
83 20
            $output->writeln('');
84
85
            /// @todo if we are passed a path, we could give the user a more specific warning than what we get back from the storage layer
86 20
            $migration = $migrationService->getMigration($migrationNameOrPath);
87 20
            if ($migration == null) {
88
                throw new \InvalidArgumentException(sprintf('The migration "%s" does not exist in the migrations table. You can use `kaliop:migration:status --show-path` to find out more', $migrationNameOrPath));
89
            }
90
91 20
            switch ($migration->status) {
92
                case Migration::STATUS_DONE:
93 18
                    $status = '<info>executed</info>';
94 18
                    break;
95
                case Migration::STATUS_STARTED:
96
                    $status = '<comment>execution started</comment>';
97
                    break;
98
                case Migration::STATUS_TODO:
99
                    // bold to-migrate!
100 1
                    $status = '<error>not executed</error>';
101 1
                    break;
102
                case Migration::STATUS_SKIPPED:
103 1
                    $status = '<comment>skipped</comment>';
104 1
                    break;
105
                case Migration::STATUS_PARTIALLY_DONE:
106
                    $status = '<comment>partially executed</comment>';
107
                    break;
108
                case Migration::STATUS_SUSPENDED:
109
                    $status = '<comment>suspended</comment>';
110
                    break;
111
                case Migration::STATUS_FAILED:
112
                    $status = '<error>failed</error>';
113
                    break;
114
            }
115
116 20
            $output->writeln('<info>Migration: ' . $migration->name . '</info>');
117 20
            $output->writeln('Status: ' . $status);
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $status does not seem to be defined for all execution paths leading up to this point.
Loading history...
118 20
            $output->writeln('Executed on: <info>' . ($migration->executionDate != null ? date("Y-m-d H:i:s", $migration->executionDate) : '--'). '</info>');
119 20
            $output->writeln('Execution notes: <info>' . $migration->executionError . '</info>');
120
121 20
            if ($migration->status == Migration::STATUS_SUSPENDED) {
122
                /// @todo decode the suspension context: date, step, ...
123
            }
124
125 20
            $output->writeln('Definition path: <info>' . $migration->path . '</info>');
126 20
            $output->writeln('Definition md5: <info>' . $migration->md5 . '</info>');
127
128 20
            if ($migration->path != '') {
129
                // q: what if we have a loader which does not work with is_file? We could probably remove this check
130
                // or, better, add a method `$migrationService->migrationDefinitionExists` / use a specific exception in getMigrationsDefinitions...
131
                // Note also that $migration->path can not be used as is, as it is usually relative to the app's root dir
132
                //if (is_file($migration->path)) {
133
                    try {
134 20
                        $migrationDefinitionCollection = $migrationService->getMigrationsDefinitions(array($migration->path));
135 20
                        if (count($migrationDefinitionCollection)) {
136 20
                            $migrationDefinition = $migrationDefinitionCollection->reset();
137 20
                            $migrationDefinition = $migrationService->parseMigrationDefinition($migrationDefinition);
138
139 20
                            if ($migrationDefinition->status != MigrationDefinition::STATUS_PARSED) {
140
                                $output->writeln('Definition error: <error>' . $migrationDefinition->parsingError . '</error>');
141
                            }
142
143 20
                            if (md5($migrationDefinition->rawDefinition) != $migration->md5) {
144 20
                                $output->writeln('Notes: <comment>The migration definition file has now a different checksum</comment>');
145
                            }
146
                        } else {
147 20
                            $output->writeln('Definition error: <error>The migration definition file can not be loaded</error>');
148
                        }
149
                    } catch (\Exception $e) {
150
                        /// @todo one day we should be able to limit the kind of exceptions we have to catch here...
151
                        $output->writeln('Definition parsing error: <error>' . $e->getMessage() . '</error>');
152
                    }
153
                //} else {
154
                //    $output->writeln('Definition error: <error>The migration definition file can not be found any more</error>');
155
                //}
156
            }
157
158 20
            $output->writeln('');
159 20
            return 0;
160
        }
161
162
        // ask user for confirmation to make changes
163 107
        if ($input->isInteractive() && !$input->getOption('no-interaction')) {
164
            $dialog = $this->getHelperSet()->get('question');
165
            if (!$dialog->ask(
0 ignored issues
show
Bug introduced by
The method ask() does not exist on Symfony\Component\Console\Helper\HelperInterface. It seems like you code against a sub-type of Symfony\Component\Console\Helper\HelperInterface such as Symfony\Component\Console\Helper\DialogHelper. ( Ignorable by Annotation )

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

165
            if (!$dialog->/** @scrutinizer ignore-call */ ask(
Loading history...
166
                $input,
167
                $output,
168
                new ConfirmationQuestion('<question>Careful, the database will be modified. Do you want to continue Y/N ?</question>', false)
169
            )
170
            ) {
171
                $output->writeln('<error>Migration change cancelled!</error>');
172
                return 0;
173
            }
174
        }
175
176 107
        if ($input->getOption('add')) {
177
            // will throw if a file is passed and it is not found, but not if an empty dir is passed
178 84
            $migrationDefinitionCollection = $migrationService->getMigrationsDefinitions(array($migrationNameOrPath));
179
180 84
            if (!count($migrationDefinitionCollection))
181
            {
182
                throw new \InvalidArgumentException(sprintf('The path "%s" does not correspond to any migration definition.', $migrationNameOrPath));
183
            }
184
185 84
            foreach ($migrationDefinitionCollection as $migrationDefinition) {
186 84
                $migrationName = basename($migrationDefinition->path);
187
188 84
                $migration = $migrationService->getMigration($migrationNameOrPath);
189 84
                if ($migration != null) {
190
                    throw new \InvalidArgumentException(sprintf('The migration "%s" does already exist in the migrations table.', $migrationName));
191
                }
192
193 84
                $migrationService->addMigration($migrationDefinition);
194 84
                $output->writeln('<info>Added migration ' . $migrationDefinition->path . '</info>');
195
            }
196
197 84
            return 0;
198
        }
199
200 107
        if ($input->getOption('delete') || $input->getOption('fail')) {
201
            /// @todo if we are passed a path, we could give the user a more specific warning than what we get back from the storage layer
202 107
            $migration = $migrationService->getMigration($migrationNameOrPath);
203 107
            if ($migration == null) {
204 104
                throw new \InvalidArgumentException(sprintf('The migration "%s" does not exist in the migrations table.', $migrationNameOrPath));
205
            }
206
207 107
            if ($input->getOption('delete')) {
208 107
                $migrationService->deleteMigration($migration);
209
            } else {
210
                $errorMessage = 'Manually failed on ' . date("Y-m-d H:i:s");
211
                if ($migration->executionError != '') {
212
                    $errorMessage .= ". Previous notes: " . $migration->executionError;
213
                }
214
                $migrationService->failMigration($migration, $errorMessage);
215
            }
216
217
            return 0;
218 107
        }
219
220
        if ($input->getOption('skip')) {
221 1
            // will throw if a file is passed and it is not found, but not if an empty dir is passed
222
            $migrationDefinitionCollection = $migrationService->getMigrationsDefinitions(array($migrationNameOrPath));
223 1
224
            if (!count($migrationDefinitionCollection))
225 1
            {
226
                throw new \InvalidArgumentException(sprintf('The path "%s" does not correspond to any migration definition.', $migrationNameOrPath));
227
            }
228
229
            foreach ($migrationDefinitionCollection as $migrationDefinition) {
230 1
                $migrationService->skipMigration($migrationDefinition);
231 1
                $output->writeln('<info>Migration ' . $migrationDefinition->path . ' marked as skipped</info>');
232 1
            }
233
234
            return 0;
235 1
        }
236
237
        throw new \InvalidArgumentException("Please specify one action to be taken on the given migration");
238
    }
239
}
240