Skip to content

Commit 065b2d4

Browse files
committed
refactor: migrate make:migration to AbstractGeneratorCommand
1 parent fafb320 commit 065b2d4

6 files changed

Lines changed: 195 additions & 128 deletions

File tree

‎system/Commands/Generators/MigrationGenerator.php‎

Lines changed: 82 additions & 96 deletions
Original file line numberDiff line numberDiff line change
@@ -13,86 +13,71 @@
1313

1414
namespace CodeIgniter\Commands\Generators;
1515

16-
use CodeIgniter\CLI\BaseCommand;
16+
use CodeIgniter\CLI\AbstractGeneratorCommand;
17+
use CodeIgniter\CLI\Attributes\Command;
18+
use CodeIgniter\CLI\Attributes\GeneratorCommand;
1719
use CodeIgniter\CLI\CLI;
18-
use CodeIgniter\CLI\GeneratorTrait;
20+
use CodeIgniter\CLI\Input\Option;
1921
use Config\Database;
2022
use Config\Migrations;
2123
use Config\Session as SessionConfig;
2224

23-
/**
24-
* Generates a skeleton migration file.
25-
*/
26-
class MigrationGenerator extends BaseCommand
25+
#[Command(name: 'make:migration', description: 'Generates a new migration file.', group: 'Generators')]
26+
#[GeneratorCommand(
27+
component: 'Migration',
28+
template: 'migration.tpl.php',
29+
directory: 'Database\Migrations',
30+
classNameLang: 'CLI.generator.className.migration',
31+
)]
32+
class MigrationGenerator extends AbstractGeneratorCommand
2733
{
28-
use GeneratorTrait;
29-
30-
/**
31-
* The Command's Group
32-
*
33-
* @var string
34-
*/
35-
protected $group = 'Generators';
36-
37-
/**
38-
* The Command's Name
39-
*
40-
* @var string
41-
*/
42-
protected $name = 'make:migration';
43-
44-
/**
45-
* The Command's Description
46-
*
47-
* @var string
48-
*/
49-
protected $description = 'Generates a new migration file.';
50-
51-
/**
52-
* The Command's Usage
53-
*
54-
* @var string
55-
*/
56-
protected $usage = 'make:migration <name> [options]';
57-
58-
/**
59-
* The Command's Arguments
60-
*
61-
* @var array<string, string>
62-
*/
63-
protected $arguments = [
64-
'name' => 'The migration class name.',
65-
];
66-
67-
/**
68-
* The Command's Options
69-
*
70-
* @var array<string, string>
71-
*/
72-
protected $options = [
73-
'--session' => 'Generates the migration file for database sessions.',
74-
'--table' => 'Table name to use for database sessions. Default: "ci_sessions".',
75-
'--dbgroup' => 'Database group to use for database sessions. Default: "default".',
76-
'--namespace' => 'Set root namespace. Default: "APP_NAMESPACE".',
77-
'--suffix' => 'Append the component title to the class name (e.g. User => UserMigration).',
78-
];
79-
80-
/**
81-
* Actually execute a command.
82-
*/
83-
public function run(array $params)
34+
protected function configure(): void
35+
{
36+
parent::configure();
37+
38+
$this
39+
->addOption(new Option(
40+
name: 'session',
41+
description: 'Generate the migration file for database sessions.',
42+
))
43+
->addOption(new Option(
44+
name: 'table',
45+
shortcut: 't',
46+
description: 'Table name to use for database sessions.',
47+
requiresValue: true,
48+
default: 'ci_sessions',
49+
))
50+
->addOption(new Option(
51+
name: 'dbgroup',
52+
shortcut: 'g',
53+
description: 'Database group to use for database sessions.',
54+
requiresValue: true,
55+
valueLabel: 'group',
56+
default: 'default',
57+
));
58+
}
59+
60+
protected function provideGeneratorOptions(): void
61+
{
62+
$this->addNamespaceOption()->addSuffixOption();
63+
}
64+
65+
protected function initialize(array &$arguments, array &$options): void
8466
{
85-
$this->component = 'Migration';
86-
$this->directory = 'Database\Migrations';
87-
$this->template = 'migration.tpl.php';
67+
if (! $this->hasUnboundOption('session', $options)) {
68+
return;
69+
}
8870

89-
if (array_key_exists('session', $params) || CLI::getOption('session')) {
90-
$table = $params['table'] ?? CLI::getOption('table') ?? 'ci_sessions';
91-
$params[0] = "_create_{$table}_table";
71+
$table = $this->getUnboundOption('table', $options);
9272

93-
$group = $params['dbgroup'] ?? CLI::getOption('dbgroup');
94-
$group = is_string($group) ? $group : 'default';
95-
$driver = config(Database::class)->{$group}['DBDriver'] ?? null;
73+
$arguments[0] = sprintf('_create_%s_table', is_string($table) ? $table : 'ci_sessions');
74+
}
75+
76+
protected function execute(array $arguments, array $options): int
77+
{
78+
if ($this->getValidatedOption('session') === true) {
79+
$group = $this->getDatabaseGroup();
80+
$driver = $this->getDatabaseDriver($group);
9681

9782
if ($driver === null) {
9883
CLI::error(lang('CLI.generator.undefinedDatabaseGroup', [$group]));
@@ -107,40 +92,41 @@ public function run(array $params)
10792
}
10893
}
10994

110-
$this->classNameLang = 'CLI.generator.className.migration';
111-
$this->generateClass($params);
112-
113-
return EXIT_SUCCESS;
95+
return $this->generateClass();
11496
}
11597

116-
/**
117-
* Prepare options and do the necessary replacements.
118-
*/
119-
protected function prepare(string $class): string
98+
protected function getTemplateData(string $class): array
12099
{
121-
$data = [];
122-
$data['session'] = false;
123-
124-
if ($this->getOption('session')) {
125-
$table = $this->getOption('table');
126-
$DBGroup = $this->getOption('dbgroup');
127-
128-
$data['session'] = true;
129-
$data['table'] = is_string($table) ? $table : 'ci_sessions';
130-
$data['DBGroup'] = is_string($DBGroup) ? $DBGroup : 'default';
131-
$data['DBDriver'] = config(Database::class)->{$data['DBGroup']}['DBDriver'];
132-
133-
$data['matchIP'] = config(SessionConfig::class)->matchIP;
100+
if ($this->getValidatedOption('session') !== true) {
101+
return ['session' => false];
134102
}
135103

136-
return $this->parseTemplate($class, [], [], $data);
104+
$group = $this->getDatabaseGroup();
105+
106+
return [
107+
'session' => true,
108+
'table' => $this->getValidatedOption('table'),
109+
'DBGroup' => $group,
110+
'DBDriver' => $this->getDatabaseDriver($group),
111+
'matchIP' => config(SessionConfig::class)->matchIP,
112+
];
137113
}
138114

139-
/**
140-
* Change file basename before saving.
141-
*/
142115
protected function basename(string $filename): string
143116
{
144117
return gmdate(config(Migrations::class)->timestampFormat) . basename($filename);
145118
}
119+
120+
private function getDatabaseGroup(): string
121+
{
122+
$group = $this->getValidatedOption('dbgroup');
123+
assert(is_string($group));
124+
125+
return $group;
126+
}
127+
128+
private function getDatabaseDriver(string $group): ?string
129+
{
130+
return config(Database::class)->{$group}['DBDriver'] ?? null;
131+
}
146132
}

‎system/Commands/Generators/ScaffoldGenerator.php‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,7 @@ public function run(array $params)
119119
// Call those commands!
120120
$exit1 = $this->call('make:controller', array_merge([$class], $controllerOpts, $options));
121121
$exit2 = $this->call('make:model', array_merge([$class], $modelOpts, $options));
122-
$exit3 = $this->call('make:migration', array_merge([$class], $options));
122+
$exit3 = $this->call('make:migration', array_merge([$class], array_diff_key($options, ['force' => null])));
123123
$exit4 = $this->call('make:seeder', array_merge([$class], $options));
124124

125125
assert(is_int($exit1) && is_int($exit2) && is_int($exit3) && is_int($exit4));

‎tests/system/Commands/Generators/MigrationGeneratorTest.php‎

Lines changed: 97 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313

1414
namespace CodeIgniter\Commands\Generators;
1515

16+
use CodeIgniter\CLI\CLI;
1617
use CodeIgniter\Config\Factories;
1718
use CodeIgniter\Test\CIUnitTestCase;
1819
use CodeIgniter\Test\StreamFilterTrait;
@@ -27,61 +28,137 @@ final class MigrationGeneratorTest extends CIUnitTestCase
2728
{
2829
use StreamFilterTrait;
2930

31+
protected function setUp(): void
32+
{
33+
parent::setUp();
34+
35+
CLI::reset();
36+
}
37+
3038
protected function tearDown(): void
3139
{
40+
parent::tearDown();
41+
42+
CLI::reset();
3243
Factories::reset('config');
3344

34-
$result = str_replace(["\033[0;32m", "\033[0m", "\n"], '', $this->getStreamFilterBuffer());
35-
$file = str_replace('APPPATH' . DIRECTORY_SEPARATOR, APPPATH, trim(substr($result, 14)));
36-
if (is_file($file)) {
45+
foreach (glob(APPPATH . 'Database/Migrations/*_*.php') as $file) {
3746
unlink($file);
3847
}
3948
}
4049

50+
private function getUndecoratedBuffer(): string
51+
{
52+
return preg_replace('/\e\[[^m]+m/', '', $this->getStreamFilterBuffer()) ?? '';
53+
}
54+
55+
private function getContents(string $basename): string
56+
{
57+
$files = glob(APPPATH . 'Database/Migrations/*_' . $basename . '.php');
58+
$this->assertCount(1, $files);
59+
60+
$contents = file_get_contents($files[0]);
61+
$this->assertIsString($contents);
62+
63+
return $contents;
64+
}
65+
66+
private function injectSessionsGroup(string $driver): void
67+
{
68+
$config = new class () extends Database {
69+
/**
70+
* @var array<string, string>
71+
*/
72+
public array $sessions = [];
73+
};
74+
$config->sessions['DBDriver'] = $driver;
75+
76+
Factories::injectMock('config', 'Database', $config);
77+
}
78+
4179
public function testGenerateMigration(): void
4280
{
4381
command('make:migration database');
44-
$this->assertStringContainsString('_Database.php', $this->getStreamFilterBuffer());
82+
83+
$this->assertMatchesRegularExpression(
84+
'#^\nFile created: APPPATH/Database/Migrations/\d{4}-\d{2}-\d{2}-\d{6}_Database\.php\n$#',
85+
$this->getUndecoratedBuffer(),
86+
);
87+
88+
$contents = $this->getContents('Database');
89+
$this->assertStringContainsString('namespace App\Database\Migrations;', $contents);
90+
$this->assertStringContainsString('class Database extends Migration', $contents);
91+
$this->assertStringNotContainsString('$DBGroup', $contents);
4592
}
4693

4794
public function testGenerateMigrationWithOptionSession(): void
4895
{
49-
command('make:migration -session');
50-
$this->assertStringContainsString('_CreateCiSessionsTable.php', $this->getStreamFilterBuffer());
96+
command('make:migration --session');
97+
98+
$contents = $this->getContents('CreateCiSessionsTable');
99+
$this->assertStringContainsString('class CreateCiSessionsTable extends Migration', $contents);
100+
$this->assertStringContainsString("protected \$DBGroup = 'default';", $contents);
101+
$this->assertStringContainsString("\$this->forge->addKey('id', true);", $contents);
102+
$this->assertStringContainsString("\$this->forge->createTable('ci_sessions', true);", $contents);
51103
}
52104

53-
public function testGenerateMigrationWithOptionTable(): void
105+
public function testSessionIgnoresNameArgument(): void
54106
{
55-
command('make:migration -session -table logger');
56-
$this->assertStringContainsString('_CreateLoggerTable.php', $this->getStreamFilterBuffer());
107+
command('make:migration database --session');
108+
109+
$this->assertStringContainsString('_CreateCiSessionsTable.php', $this->getUndecoratedBuffer());
110+
$this->assertFileDoesNotExist(APPPATH . 'Database/Migrations/Database.php');
111+
}
112+
113+
public function testGenerateMigrationWithOptionTableAndDbGroup(): void
114+
{
115+
$this->injectSessionsGroup('Postgre');
116+
117+
command('make:migration --session --table logger --dbgroup sessions');
118+
119+
$contents = $this->getContents('CreateLoggerTable');
120+
$this->assertStringContainsString("protected \$DBGroup = 'sessions';", $contents);
121+
$this->assertStringContainsString("'ip_address inet NOT NULL',", $contents);
122+
$this->assertStringContainsString("\$this->forge->createTable('logger', true);", $contents);
123+
$this->assertStringContainsString("\$this->forge->dropTable('logger', true);", $contents);
124+
}
125+
126+
public function testGenerateMigrationWithShortcuts(): void
127+
{
128+
$this->injectSessionsGroup('MySQLi');
129+
130+
command('make:migration --session -t logger -g sessions');
131+
132+
$contents = $this->getContents('CreateLoggerTable');
133+
$this->assertStringContainsString("protected \$DBGroup = 'sessions';", $contents);
134+
$this->assertStringContainsString("\$this->forge->createTable('logger', true);", $contents);
57135
}
58136

59137
public function testSessionRejectsUnsupportedDriver(): void
60138
{
61-
$config = new Database();
62-
$config->default['DBDriver'] = 'SQLite3';
63-
Factories::injectMock('config', 'Database', $config);
139+
$this->injectSessionsGroup('SQLite3');
64140

65-
command('make:migration -session');
141+
command('make:migration --session --dbgroup sessions');
66142

67-
$this->assertStringContainsString(
68-
'Database sessions are only supported on MySQLi and Postgre. The "default" database group uses the "SQLite3" driver.',
69-
$this->getStreamFilterBuffer(),
143+
$this->assertSame(
144+
"\nDatabase sessions are only supported on MySQLi and Postgre. The \"sessions\" database group uses the \"SQLite3\" driver.\n",
145+
$this->getUndecoratedBuffer(),
70146
);
71147
$this->assertSame([], glob(APPPATH . 'Database/Migrations/*_CreateCiSessionsTable.php'));
72148
}
73149

74150
public function testSessionRejectsUndefinedGroup(): void
75151
{
76-
command('make:migration -session -dbgroup bogus');
152+
command('make:migration --session --dbgroup bogus');
77153

78-
$this->assertStringContainsString('The "bogus" database group is not defined.', $this->getStreamFilterBuffer());
154+
$this->assertSame("\nThe \"bogus\" database group is not defined.\n", $this->getUndecoratedBuffer());
79155
$this->assertSame([], glob(APPPATH . 'Database/Migrations/*_CreateCiSessionsTable.php'));
80156
}
81157

82158
public function testGenerateMigrationWithOptionSuffix(): void
83159
{
84-
command('make:migration database -suffix');
85-
$this->assertStringContainsString('_DatabaseMigration.php', $this->getStreamFilterBuffer());
160+
command('make:migration database --suffix');
161+
162+
$this->assertStringContainsString('class DatabaseMigration extends Migration', $this->getContents('DatabaseMigration'));
86163
}
87164
}

0 commit comments

Comments
 (0)