Skip to content

Commit a4543bb

Browse files
authored
refactor: migrate make:cell to AbstractGeneratorCommand (#10551)
1 parent 961d0ce commit a4543bb

5 files changed

Lines changed: 124 additions & 125 deletions

File tree

system/Commands/Generators/CellGenerator.php

Lines changed: 36 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -13,95 +13,56 @@
1313

1414
namespace CodeIgniter\Commands\Generators;
1515

16-
use CodeIgniter\CLI\BaseCommand;
17-
use CodeIgniter\CLI\GeneratorTrait;
16+
use CodeIgniter\CLI\AbstractGeneratorCommand;
17+
use CodeIgniter\CLI\Attributes\Command;
18+
use CodeIgniter\CLI\Attributes\GeneratorCommand;
1819
use Config\Generators;
1920

20-
/**
21-
* Generates a skeleton Cell and its view.
22-
*/
23-
class CellGenerator extends BaseCommand
21+
#[Command(name: 'make:cell', description: 'Generates a new Controlled Cell file and its view.', group: 'Generators')]
22+
#[GeneratorCommand(
23+
component: 'Cell',
24+
template: 'cell.tpl.php',
25+
directory: 'Cells',
26+
classNameLang: 'CLI.generator.className.cell',
27+
)]
28+
class CellGenerator extends AbstractGeneratorCommand
2429
{
25-
use GeneratorTrait;
30+
protected function provideGeneratorOptions(): void
31+
{
32+
$this->addNamespaceOption()->addForceOption();
33+
}
2634

27-
/**
28-
* The Command's Group
29-
*
30-
* @var string
31-
*/
32-
protected $group = 'Generators';
35+
protected function shouldAppendSuffix(): bool
36+
{
37+
return true;
38+
}
3339

34-
/**
35-
* The Command's Name
36-
*
37-
* @var string
38-
*/
39-
protected $name = 'make:cell';
40+
protected function execute(array $arguments, array $options): int
41+
{
42+
$views = config(Generators::class)->views[$this->getName()] ?? [];
4043

41-
/**
42-
* The Command's Description
43-
*
44-
* @var string
45-
*/
46-
protected $description = 'Generates a new Controlled Cell file and its view.';
44+
$this->templatePath = $views['class'] ?? null;
4745

48-
/**
49-
* The Command's Usage
50-
*
51-
* @var string
52-
*/
53-
protected $usage = 'make:cell <name> [options]';
46+
$classExitCode = $this->generateClass();
5447

55-
/**
56-
* The Command's Arguments
57-
*
58-
* @var array<string, string>
59-
*/
60-
protected $arguments = [
61-
'name' => 'The Controlled Cell class name.',
62-
];
48+
$this->templatePath = $views['view'] ?? null;
49+
$this->template = 'cell_view.tpl.php';
6350

64-
/**
65-
* The Command's Options
66-
*
67-
* @var array<string, string>
68-
*/
69-
protected $options = [
70-
'--namespace' => 'Set root namespace. Default: "APP_NAMESPACE".',
71-
'--force' => 'Force overwrite existing file.',
72-
];
51+
$viewExitCode = $this->generateView($this->getViewName($this->qualifyClassName()));
52+
53+
return $classExitCode | $viewExitCode;
54+
}
7355

7456
/**
75-
* Actually execute a command.
57+
* Derives the namespaced view name from the qualified cell class, dropping the `Cell` suffix.
7658
*/
77-
public function run(array $params)
59+
private function getViewName(string $class): string
7860
{
79-
$this->component = 'Cell';
80-
$this->directory = 'Cells';
81-
82-
$params = array_merge($params, ['suffix' => null]);
83-
84-
$this->templatePath = config(Generators::class)->views[$this->name]['class'];
85-
$this->template = 'cell.tpl.php';
86-
$this->classNameLang = 'CLI.generator.className.cell';
87-
88-
$this->generateClass($params);
89-
90-
$this->templatePath = config(Generators::class)->views[$this->name]['view'];
91-
$this->template = 'cell_view.tpl.php';
92-
$this->classNameLang = 'CLI.generator.viewName.cell';
93-
94-
$className = $this->qualifyClassName();
95-
$viewName = decamelize(class_basename($className));
96-
$viewName = preg_replace(
97-
'/([a-z][a-z0-9_\/\\\\]+)(_cell)$/i',
98-
'$1',
99-
$viewName,
100-
) ?? $viewName;
101-
$namespace = substr($className, 0, strrpos($className, '\\') + 1);
61+
$segments = explode('\\', $class);
62+
$basename = decamelize(array_pop($segments));
10263

103-
$this->generateView($namespace . $viewName, $params);
64+
$segments[] = preg_replace('/_cell$/', '', $basename) ?? $basename;
10465

105-
return EXIT_SUCCESS;
66+
return implode('\\', $segments);
10667
}
10768
}

system/Language/en/CLI.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,8 @@
5050
'returnType' => 'Return type',
5151
'tableName' => 'Table name',
5252
'usingCINamespace' => 'Warning: Using the "CodeIgniter" namespace will generate the file in the system directory.',
53-
'viewName' => [
53+
// @deprecated v4.8.0 - never used
54+
'viewName' => [
5455
'cell' => 'Cell view name',
5556
],
5657
],

tests/system/Commands/Generators/CellGeneratorTest.php

Lines changed: 83 additions & 47 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\Test\CIUnitTestCase;
1718
use CodeIgniter\Test\StreamFilterTrait;
1819
use PHPUnit\Framework\Attributes\Group;
@@ -25,77 +26,112 @@ final class CellGeneratorTest extends CIUnitTestCase
2526
{
2627
use StreamFilterTrait;
2728

29+
protected function setUp(): void
30+
{
31+
parent::setUp();
32+
33+
CLI::reset();
34+
}
35+
2836
protected function tearDown(): void
2937
{
30-
$dirName = APPPATH . DIRECTORY_SEPARATOR . 'Cells';
31-
// remove dir
32-
if (is_dir($dirName)) {
33-
$files = array_diff(scandir($dirName), ['.', '..']);
34-
35-
foreach ($files as $file) {
36-
(is_dir("{$dirName}/{$file}")) ? rmdir("{$dirName}/{$file}") : unlink("{$dirName}/{$file}");
37-
}
38-
rmdir($dirName);
38+
parent::tearDown();
39+
40+
CLI::reset();
41+
42+
$dir = APPPATH . 'Cells';
43+
44+
if (is_dir($dir)) {
45+
helper('filesystem');
46+
delete_files($dir, true, false, true);
47+
rmdir($dir);
3948
}
4049
}
4150

42-
protected function getFileContents(string $filepath): string
51+
private function getUndecoratedBuffer(): string
4352
{
44-
if (! is_file($filepath)) {
45-
return '';
46-
}
53+
return preg_replace('/\e\[[^m]+m/', '', $this->getStreamFilterBuffer()) ?? '';
54+
}
4755

48-
return (string) file_get_contents($filepath);
56+
private function getContents(string $file): string
57+
{
58+
$contents = file_get_contents(APPPATH . 'Cells/' . $file);
59+
$this->assertIsString($contents);
60+
61+
return $contents;
62+
}
63+
64+
private function assertCellGenerated(string $class, string $view): void
65+
{
66+
$this->assertSame(
67+
sprintf("\nFile created: APPPATH/Cells/%s.php\nFile created: APPPATH/Cells/%s.php\n", $class, $view),
68+
$this->getUndecoratedBuffer(),
69+
);
70+
$this->assertStringContainsString(sprintf('class %s extends Cell', class_basename($class)), $this->getContents($class . '.php'));
71+
$this->assertSame("<div>\n <!-- Your HTML here -->\n</div>\n", $this->getContents($view . '.php'));
4972
}
5073

5174
public function testGenerateCell(): void
5275
{
5376
command('make:cell RecentCell');
5477

55-
// Check the class was generated
56-
$file = APPPATH . 'Cells/RecentCell.php';
57-
$this->assertStringContainsString('File created: ' . clean_path($file), $this->getStreamFilterBuffer());
58-
$this->assertFileExists($file);
59-
$this->assertStringContainsString('class RecentCell extends Cell', $this->getFileContents($file));
60-
61-
// Check the view was generated
62-
$file = APPPATH . 'Cells/recent.php';
63-
$this->assertStringContainsString('File created: ' . clean_path($file), $this->getStreamFilterBuffer());
64-
$this->assertFileExists($file);
65-
$this->assertSame("<div>\n <!-- Your HTML here -->\n</div>\n", $this->getFileContents($file));
78+
$this->assertCellGenerated('RecentCell', 'recent');
6679
}
6780

6881
public function testGenerateCellSimpleName(): void
6982
{
7083
command('make:cell Another');
7184

72-
// Check the class was generated
73-
$file = APPPATH . 'Cells/AnotherCell.php';
74-
$this->assertStringContainsString('File created: ' . clean_path($file), $this->getStreamFilterBuffer());
75-
$this->assertFileExists($file);
76-
$this->assertStringContainsString('class AnotherCell extends Cell', $this->getFileContents($file));
77-
78-
// Check the view was generated
79-
$file = APPPATH . 'Cells/another.php';
80-
$this->assertStringContainsString('File created: ' . clean_path($file), $this->getStreamFilterBuffer());
81-
$this->assertFileExists($file);
82-
$this->assertSame("<div>\n <!-- Your HTML here -->\n</div>\n", $this->getFileContents($file));
85+
$this->assertCellGenerated('AnotherCell', 'another');
8386
}
8487

8588
public function testGenerateCellWithCellInBetween(): void
8689
{
8790
command('make:cell PippoCellular');
8891

89-
// Check the class was generated
90-
$file = APPPATH . 'Cells/PippoCellularCell.php';
91-
$this->assertStringContainsString('File created: ' . clean_path($file), $this->getStreamFilterBuffer());
92-
$this->assertFileExists($file);
93-
$this->assertStringContainsString('class PippoCellularCell extends Cell', $this->getFileContents($file));
94-
95-
// Check the view was generated
96-
$file = APPPATH . 'Cells/pippo_cellular.php';
97-
$this->assertStringContainsString('File created: ' . clean_path($file), $this->getStreamFilterBuffer());
98-
$this->assertFileExists($file);
99-
$this->assertSame("<div>\n <!-- Your HTML here -->\n</div>\n", $this->getFileContents($file));
92+
$this->assertCellGenerated('PippoCellularCell', 'pippo_cellular');
93+
}
94+
95+
public function testGenerateCellInSubNamespace(): void
96+
{
97+
command('make:cell admin/stats');
98+
99+
$this->assertCellGenerated('Admin/StatsCell', 'Admin/stats');
100+
$this->assertStringContainsString('namespace App\Cells\Admin;', $this->getContents('Admin/StatsCell.php'));
101+
}
102+
103+
public function testExistingClassStillGeneratesMissingView(): void
104+
{
105+
command('make:cell RecentCell');
106+
unlink(APPPATH . 'Cells/recent.php');
107+
$this->resetStreamFilterBuffer();
108+
109+
command('make:cell RecentCell');
110+
111+
$this->assertSame(
112+
<<<'EOT'
113+
File exists: "APPPATH/Cells/RecentCell.php"
114+
File created: APPPATH/Cells/recent.php
115+
116+
EOT,
117+
$this->getUndecoratedBuffer(),
118+
);
119+
}
120+
121+
public function testForceOverwritesBothFiles(): void
122+
{
123+
command('make:cell RecentCell');
124+
$this->resetStreamFilterBuffer();
125+
126+
command('make:cell RecentCell --force');
127+
128+
$this->assertSame(
129+
<<<'EOT'
130+
File overwritten: "APPPATH/Cells/RecentCell.php"
131+
File overwritten: "APPPATH/Cells/recent.php"
132+
133+
EOT,
134+
$this->getUndecoratedBuffer(),
135+
);
100136
}
101137
}

user_guide_src/source/changelogs/v4.8.0.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -414,6 +414,7 @@ Deprecations
414414
- **CLI:** Returning a non-integer exit code from a command is now deprecated and will trigger a deprecation notice. Command methods should return an integer exit code (e.g., ``0`` for success, non-zero for errors) to ensure proper behavior across all platforms.
415415
- **CLI:** ``Commands::run()`` is now deprecated in favor of ``Commands::runLegacy()`` for legacy ``BaseCommand`` commands, and ``Commands::runCommand()`` for modern ``AbstractCommand`` commands.
416416
- **CLI:** The ``$commands`` parameter of ``Commands::verifyCommand()`` and the ``$collection`` parameter of ``Commands::getCommandAlternatives()`` are no longer used. Passing a non-empty array for either will trigger a deprecation notice.
417+
- **CLI:** The ``CLI.generator.viewName.cell`` language string is deprecated. It was never displayed, since ``make:cell`` only prompts for the class name.
417418
- **HTTP:** The ``CLIRequest::parseCommand()`` method is now deprecated and will be removed in a future release. The ``CLIRequest`` class now uses the new ``CommandLineParser`` class to handle command-line argument parsing.
418419
- **HTTP:** ``URI::setSilent()`` is now hard deprecated. This method was only previously marked as deprecated. It will now trigger a deprecation notice when used.
419420

user_guide_src/source/cli/cli_generators.rst

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -64,8 +64,8 @@ Argument:
6464

6565
Options:
6666
========
67-
* ``--namespace``: Set the root namespace. Defaults to value of ``APP_NAMESPACE``.
68-
* ``--force``: Set this flag to overwrite existing files on destination.
67+
* ``--namespace`` (``-n``): Set the root namespace. Defaults to value of ``APP_NAMESPACE``.
68+
* ``--force`` (``-f``): Set this flag to overwrite existing files on destination.
6969

7070
make:command
7171
------------

0 commit comments

Comments
 (0)