CreateMigration   A
last analyzed

Complexity

Total Complexity 13

Size/Duplication

Total Lines 105
Duplicated Lines 0 %

Test Coverage

Coverage 0%

Importance

Changes 0
Metric Value
eloc 82
dl 0
loc 105
ccs 0
cts 46
cp 0
rs 10
c 0
b 0
f 0
wmc 13

7 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 6 1
B execute() 0 43 7
A promptName() 0 6 1
A promptCrate() 0 5 1
A promptDir() 0 12 1
A configure() 0 9 1
A migrationTemplate() 0 3 1
1
<?php declare(strict_types=1);
2
/**
3
 * This file is part of the daikon-cqrs/boot project.
4
 *
5
 * For the full copyright and license information, please view the LICENSE
6
 * file that was distributed with this source code.
7
 */
8
9
namespace Daikon\Boot\Console\Command\Migrate;
10
11
use Daikon\Boot\Console\Command\DialogTrait;
12
use Daikon\Boot\Crate\CrateInterface;
13
use Daikon\Boot\Crate\CrateMap;
14
use Daikon\Dbal\Migration\MigrationTargetMap;
15
use Daikon\Interop\Assertion;
16
use DateTimeImmutable;
17
use Stringy\Stringy;
18
use Symfony\Component\Console\Command\Command;
0 ignored issues
show
Bug introduced by
The type Symfony\Component\Console\Command\Command 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...
19
use Symfony\Component\Console\Input\InputArgument;
20
use Symfony\Component\Console\Input\InputInterface;
21
use Symfony\Component\Console\Output\OutputInterface;
22
use Symfony\Component\Console\Question\ChoiceQuestion;
23
use Symfony\Component\Console\Question\Question;
0 ignored issues
show
Bug introduced by
The type Symfony\Component\Console\Question\Question 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...
24
use Symfony\Component\Finder\Finder;
25
use Symfony\Component\Finder\SplFileInfo;
26
27
final class CreateMigration extends Command
28
{
29
    use DialogTrait;
30
31
    private MigrationTargetMap $migrationTargetMap;
32
33
    private CrateMap $crateMap;
34
35
    public function __construct(MigrationTargetMap $migrationTargetMap, CrateMap $crateMap)
36
    {
37
        $this->migrationTargetMap = $migrationTargetMap;
38
        $this->crateMap = $crateMap;
39
40
        parent::__construct();
41
    }
42
43
    protected function configure(): void
44
    {
45
        $this
46
            ->setName('migrate:create')
47
            ->setDescription('Create a new migration within a selected crate.')
48
            ->addArgument(
49
                'crate',
50
                InputArgument::OPTIONAL,
51
                'Name of the crate to create the migration in.'
52
            );
53
    }
54
55
    protected function execute(InputInterface $input, OutputInterface $output): int
56
    {
57
        if (!count($this->crateMap) || !count($this->migrationTargetMap)) {
58
            $output->writeln('<error>There are no targets available to generate migrations for.</error>');
59
            $output->writeln('');
60
            exit;
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
Bug Best Practice introduced by
In this branch, the function will implicitly return null which is incompatible with the type-hinted return integer. Consider adding a return statement or allowing null as return value.

For hinted functions/methods where all return statements with the correct type are only reachable via conditions, ?null? gets implicitly returned which may be incompatible with the hinted type. Let?s take a look at an example:

interface ReturnsInt {
    public function returnsIntHinted(): int;
}

class MyClass implements ReturnsInt {
    public function returnsIntHinted(): int
    {
        if (foo()) {
            return 123;
        }
        // here: null is implicitly returned
    }
}
Loading history...
61
        }
62
63
        if (!is_string($crateKey = $input->getArgument('crate'))) {
64
            $crateKey = $this->promptCrate($input, $output);
65
        }
66
67
        if (!$this->crateMap->has($crateKey)) {
68
            $output->writeln("<error>Crate '$crateKey' does not exist.</error>");
69
            $output->writeln('');
70
            exit;
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
Bug Best Practice introduced by
In this branch, the function will implicitly return null which is incompatible with the type-hinted return integer. Consider adding a return statement or allowing null as return value.

For hinted functions/methods where all return statements with the correct type are only reachable via conditions, ?null? gets implicitly returned which may be incompatible with the hinted type. Let?s take a look at an example:

interface ReturnsInt {
    public function returnsIntHinted(): int;
}

class MyClass implements ReturnsInt {
    public function returnsIntHinted(): int
    {
        if (foo()) {
            return 123;
        }
        // here: null is implicitly returned
    }
}
Loading history...
71
        }
72
73
        /** @var CrateInterface $crate */
74
        $crate = $this->crateMap->get($crateKey);
75
        $crateSettings = $crate->getSettings();
76
        $targetDir = $this->promptDir($crateSettings['migration_dir'], $input, $output);
77
78
        $timestamp = (new DateTimeImmutable)->format('Ymdhis');
79
        $name = $this->promptName($input, $output);
80
        $migrationTpl = $this->migrationTemplate();
81
        $className = $name.$timestamp;
82
        $migration = str_replace("[CLASSNAME]", $className, $migrationTpl);
83
        $migrationDir = implode('', [
84
            $crateSettings['migration_dir'],
85
            "/$targetDir/$timestamp"."_$name",
86
        ]);
87
88
        if (!is_dir($migrationDir)) {
89
            mkdir($migrationDir);
90
        }
91
92
        $migrationFile = "$migrationDir/$className.php";
93
        if (file_put_contents($migrationFile, $migration)) {
94
            $output->writeln("Created migration file at: <options=bold>$migrationFile</>");
95
        }
96
97
        return 0;
98
    }
99
100
    private function promptCrate(InputInterface $input, OutputInterface $output): string
101
    {
102
        $helper = $this->getHelper('question');
103
        $question = new ChoiceQuestion('Please select a crate: ', $this->crateMap->keys());
104
        return $helper->ask($input, $output, $question);
105
    }
106
107
    private function promptDir(string $parent, InputInterface $input, OutputInterface $output): string
108
    {
109
        $helper = $this->getHelper('question');
110
        $question = new ChoiceQuestion(
111
            'Please select a migration target dir: ',
112
            array_map(function (SplFileInfo $fileInfo): string {
113
                return $fileInfo->getBasename();
114
            }, array_values(iterator_to_array(
115
                (new Finder)->depth(0)->directories()->in($parent)
116
            )))
117
        );
118
        return $helper->ask($input, $output, $question);
119
    }
120
121
    private function promptName(InputInterface $input, OutputInterface $output): string
122
    {
123
        $name = $this->getHelper('question')->ask($input, $output, new Question(
124
            'Please provide a short migration description: '
125
        ));
126
        return (string)Stringy::create($name)->upperCamelize();
127
    }
128
129
    private function migrationTemplate(): string
130
    {
131
        return <<<MIGRATION
132
<?php declare(strict_types=1);
133
134
namespace Change\Me\Migration\MyTarget;
135
136
use Daikon\Dbal\Migration\Migration;
137
138
final class [CLASSNAME] extends Migration
139
{
140
    public function getDescription(string \$direction = self::MIGRATE_UP): string
141
    {
142
    }
143
144
    public function isReversible(): bool
145
    {
146
    }
147
148
    protected function up(): void
149
    {
150
    }
151
152
    protected function down(): void
153
    {
154
    }
155
}
156
157
MIGRATION;
158
    }
159
}
160