Passed
Push — master ( a4e384...6a021e )
by Divine Niiquaye
03:11
created

DatabaseMigrateCommand::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 1
dl 0
loc 3
ccs 0
cts 2
cp 0
crap 2
rs 10
c 0
b 0
f 0
1
<?php
2
3
declare(strict_types=1);
4
5
/*
6
 * This file is part of DivineNii opensource projects.
7
 *
8
 * PHP version 7.4 and above required
9
 *
10
 * @author    Divine Niiquaye Ibok <[email protected]>
11
 * @copyright 2019 DivineNii (https://divinenii.com/)
12
 * @license   https://opensource.org/licenses/BSD-3-Clause License
13
 *
14
 * For the full copyright and license information, please view the LICENSE
15
 * file that was distributed with this source code.
16
 */
17
18
namespace Rade\Commands\CycleORM;
19
20
use Cycle\Migrations\Migrator;
0 ignored issues
show
Bug introduced by
The type Cycle\Migrations\Migrator 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...
21
use Cycle\Migrations\State;
0 ignored issues
show
Bug introduced by
The type Cycle\Migrations\State 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...
22
use Symfony\Component\Console\Command\Command;
23
use Symfony\Component\Console\Helper\Table;
24
use Symfony\Component\Console\Input\ArrayInput;
25
use Symfony\Component\Console\Input\InputInterface;
26
use Symfony\Component\Console\Input\InputOption;
27
use Symfony\Component\Console\Output\OutputInterface;
28
use Symfony\Component\Console\Style\SymfonyStyle;
29
30
/**
31
 * Perform one or all outstanding migrations.
32
 *
33
 * @author Divine Niiquaye Ibok <[email protected]>
34
 */
35
class DatabaseMigrateCommand extends Command
36
{
37
    protected static $defaultName = 'cycle:database:migrate';
38
39
    public function __construct(private Migrator $migrator)
40
    {
41
        parent::__construct();
42
    }
43
44
    /**
45
     * {@inheritdoc}
46
     */
47
    protected function configure(): void
48
    {
49
        $this
50
            ->setDefinition([
51
                new InputOption('force', 's', InputOption::VALUE_NONE, 'Skip safe environment check'),
52
                new InputOption('one', 'o', InputOption::VALUE_NONE, 'Execute only one (first) migration'),
53
                new InputOption('init', 'i', InputOption::VALUE_NONE, 'Init Database migrations (create migrations table)'),
54
                new InputOption('status', null, InputOption::VALUE_NONE, 'Get list of all available migrations and their statuses'),
55
                new InputOption('rollback', null, InputOption::VALUE_NONE, 'Rollback multiple migrations (default) or one setting -o'),
56
                new InputOption('replay', null, InputOption::VALUE_NONE, 'Replay (down, up) one or multiple migrations'),
57
            ])
58
            ->setDescription('Perform one or all outstanding migrations')
59
        ;
60
    }
61
62
    /**
63
     * {@inheritdoc}
64
     */
65
    protected function execute(InputInterface $input, OutputInterface $output): int
66
    {
67
        $io = new SymfonyStyle($input, $output);
68
69
        if (!$this->migrator->isConfigured()) {
70
            $this->migrator->configure();
71
            $output->writeln('');
72
            $output->writeln('<info>Migrations table were successfully created</info>');
73
        }
74
75
        if ($input->getOption('init')) {
76
            goto exitCode;
77
        }
78
79
        if (!$this->verifyConfigured($output) || !$this->verifyEnvironment($input, $io)) {
80
            //Making sure migration is configured.
81
            return 1;
82
        }
83
84
        if ($input->getOption('status')) {
85
            if (empty($this->migrator->getMigrations())) {
86
                $output->writeln('<comment>No migrations were found.</comment>');
87
88
                return 1;
89
            }
90
91
            $table = new Table($output);
92
            $table = $table->setHeaders(['Migration', 'Created at', 'Executed at']);
93
94
            foreach ($this->migrator->getMigrations() as $migration) {
95
                $state = $migration->getState();
96
97
                $table->addRow([
98
                    $state->getName(),
99
                    $state->getTimeCreated()->format('Y-m-d H:i:s'),
100
                    State::STATUS_PENDING === $state->getStatus() ? '<fg=red>not executed yet</fg=red>' : '<info>' . $state->getTimeExecuted()->format('Y-m-d H:i:s') . '</info>',
101
                ]);
102
            }
103
            $table->render();
104
105
            goto exitCode;
106
        }
107
108
        $found = false;
109
        $count = $input->getOption('one') ? 1 : \PHP_INT_MAX;
110
111
        if ($input->getOption('replay')) {
112
            $input->setOption('rollback', $replay = true);
113
        }
114
115
        while ($count > 0 && ($migration = $this->migrator->{$input->getOption('rollback') ? 'rollback' : 'run'}())) {
116
            $found = true;
117
            --$count;
118
119
            $io->newLine();
120
            $output->write(\sprintf(
121
                "<info>Migration <comment>%s</comment> was successfully %s.</info>\n",
122
                $migration->getState()->getName(),
123
                $input->getOption('rollback') ? 'rolled back' : 'executed'
124
            ));
125
        }
126
127
        if (!$found) {
128
            $io->error('No outstanding migrations were found');
129
130
            return 1;
131
        }
132
133
        if (isset($replay)) {
134
            $output->writeln('');
135
136
            if ($io->confirm('Do you want to execute <info>migrations</info> immediately?', false)) {
137
                $output->writeln('Executing outstanding migration(s)...');
138
139
                return $this->getApplication()->find($this->getName())->run(new ArrayInput(['--force' => true]), $output);
0 ignored issues
show
Bug introduced by
It seems like $this->getName() can also be of type null; however, parameter $name of Symfony\Component\Console\Application::find() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

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

139
                return $this->getApplication()->find(/** @scrutinizer ignore-type */ $this->getName())->run(new ArrayInput(['--force' => true]), $output);
Loading history...
140
            }
141
        }
142
143
        exitCode:
144
        return self::SUCCESS;
145
    }
146
147
    protected function verifyConfigured(OutputInterface $output): bool
148
    {
149
        if (!$this->migrator->isConfigured()) {
150
            $output->writeln('');
151
            $output->writeln("<fg=red>Migrations are not configured yet, run '<info>migrations:init</info>' first.</fg=red>");
152
153
            return false;
154
        }
155
156
        return true;
157
    }
158
159
    /**
160
     * Check if current environment is safe to run migration.
161
     */
162
    protected function verifyEnvironment(InputInterface $input, SymfonyStyle $io): bool
163
    {
164
        if ($input->getOption('force') || $this->migrator->getConfig()->isSafe()) {
165
            //Safe to run
166
            return true;
167
        }
168
169
        $io->newLine();
170
        $io->writeln('<fg=red>Confirmation is required to run migrations!</fg=red>');
171
172
        if (!$io->confirm('Would you like to continue?', false)) {
173
            $io->writeln('<comment>Cancelling operation...</comment>');
174
175
            return false;
176
        }
177
178
        return true;
179
    }
180
}
181