Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
111 changes: 36 additions & 75 deletions system/Commands/Generators/CellGenerator.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,95 +13,56 @@

namespace CodeIgniter\Commands\Generators;

use CodeIgniter\CLI\BaseCommand;
use CodeIgniter\CLI\GeneratorTrait;
use CodeIgniter\CLI\AbstractGeneratorCommand;
use CodeIgniter\CLI\Attributes\Command;
use CodeIgniter\CLI\Attributes\GeneratorCommand;
use Config\Generators;

/**
* Generates a skeleton Cell and its view.
*/
class CellGenerator extends BaseCommand
#[Command(name: 'make:cell', description: 'Generates a new Controlled Cell file and its view.', group: 'Generators')]
#[GeneratorCommand(
component: 'Cell',
template: 'cell.tpl.php',
directory: 'Cells',
classNameLang: 'CLI.generator.className.cell',
)]
class CellGenerator extends AbstractGeneratorCommand
{
use GeneratorTrait;
protected function provideGeneratorOptions(): void
{
$this->addNamespaceOption()->addForceOption();
}

/**
* The Command's Group
*
* @var string
*/
protected $group = 'Generators';
protected function shouldAppendSuffix(): bool
{
return true;
}

/**
* The Command's Name
*
* @var string
*/
protected $name = 'make:cell';
protected function execute(array $arguments, array $options): int
{
$views = config(Generators::class)->views[$this->getName()] ?? [];

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

/**
* The Command's Usage
*
* @var string
*/
protected $usage = 'make:cell <name> [options]';
$classExitCode = $this->generateClass();

/**
* The Command's Arguments
*
* @var array<string, string>
*/
protected $arguments = [
'name' => 'The Controlled Cell class name.',
];
$this->templatePath = $views['view'] ?? null;
$this->template = 'cell_view.tpl.php';

/**
* The Command's Options
*
* @var array<string, string>
*/
protected $options = [
'--namespace' => 'Set root namespace. Default: "APP_NAMESPACE".',
'--force' => 'Force overwrite existing file.',
];
$viewExitCode = $this->generateView($this->getViewName($this->qualifyClassName()));

return $classExitCode | $viewExitCode;
}

/**
* Actually execute a command.
* Derives the namespaced view name from the qualified cell class, dropping the `Cell` suffix.
*/
public function run(array $params)
private function getViewName(string $class): string
{
$this->component = 'Cell';
$this->directory = 'Cells';

$params = array_merge($params, ['suffix' => null]);

$this->templatePath = config(Generators::class)->views[$this->name]['class'];
$this->template = 'cell.tpl.php';
$this->classNameLang = 'CLI.generator.className.cell';

$this->generateClass($params);

$this->templatePath = config(Generators::class)->views[$this->name]['view'];
$this->template = 'cell_view.tpl.php';
$this->classNameLang = 'CLI.generator.viewName.cell';

$className = $this->qualifyClassName();
$viewName = decamelize(class_basename($className));
$viewName = preg_replace(
'/([a-z][a-z0-9_\/\\\\]+)(_cell)$/i',
'$1',
$viewName,
) ?? $viewName;
$namespace = substr($className, 0, strrpos($className, '\\') + 1);
$segments = explode('\\', $class);
$basename = decamelize(array_pop($segments));

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

return EXIT_SUCCESS;
return implode('\\', $segments);
}
}
3 changes: 2 additions & 1 deletion system/Language/en/CLI.php
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,8 @@
'returnType' => 'Return type',
'tableName' => 'Table name',
'usingCINamespace' => 'Warning: Using the "CodeIgniter" namespace will generate the file in the system directory.',
'viewName' => [
// @deprecated v4.8.0 - never used
'viewName' => [
'cell' => 'Cell view name',
],
],
Expand Down
130 changes: 83 additions & 47 deletions tests/system/Commands/Generators/CellGeneratorTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@

namespace CodeIgniter\Commands\Generators;

use CodeIgniter\CLI\CLI;
use CodeIgniter\Test\CIUnitTestCase;
use CodeIgniter\Test\StreamFilterTrait;
use PHPUnit\Framework\Attributes\Group;
Expand All @@ -25,77 +26,112 @@ final class CellGeneratorTest extends CIUnitTestCase
{
use StreamFilterTrait;

protected function setUp(): void
{
parent::setUp();

CLI::reset();
}

protected function tearDown(): void
{
$dirName = APPPATH . DIRECTORY_SEPARATOR . 'Cells';
// remove dir
if (is_dir($dirName)) {
$files = array_diff(scandir($dirName), ['.', '..']);

foreach ($files as $file) {
(is_dir("{$dirName}/{$file}")) ? rmdir("{$dirName}/{$file}") : unlink("{$dirName}/{$file}");
}
rmdir($dirName);
parent::tearDown();

CLI::reset();

$dir = APPPATH . 'Cells';

if (is_dir($dir)) {
helper('filesystem');
delete_files($dir, true, false, true);
rmdir($dir);
}
}

protected function getFileContents(string $filepath): string
private function getUndecoratedBuffer(): string
{
if (! is_file($filepath)) {
return '';
}
return preg_replace('/\e\[[^m]+m/', '', $this->getStreamFilterBuffer()) ?? '';
}

return (string) file_get_contents($filepath);
private function getContents(string $file): string
{
$contents = file_get_contents(APPPATH . 'Cells/' . $file);
$this->assertIsString($contents);

return $contents;
}

private function assertCellGenerated(string $class, string $view): void
{
$this->assertSame(
sprintf("\nFile created: APPPATH/Cells/%s.php\nFile created: APPPATH/Cells/%s.php\n", $class, $view),
$this->getUndecoratedBuffer(),
);
$this->assertStringContainsString(sprintf('class %s extends Cell', class_basename($class)), $this->getContents($class . '.php'));
$this->assertSame("<div>\n <!-- Your HTML here -->\n</div>\n", $this->getContents($view . '.php'));
}

public function testGenerateCell(): void
{
command('make:cell RecentCell');

// Check the class was generated
$file = APPPATH . 'Cells/RecentCell.php';
$this->assertStringContainsString('File created: ' . clean_path($file), $this->getStreamFilterBuffer());
$this->assertFileExists($file);
$this->assertStringContainsString('class RecentCell extends Cell', $this->getFileContents($file));

// Check the view was generated
$file = APPPATH . 'Cells/recent.php';
$this->assertStringContainsString('File created: ' . clean_path($file), $this->getStreamFilterBuffer());
$this->assertFileExists($file);
$this->assertSame("<div>\n <!-- Your HTML here -->\n</div>\n", $this->getFileContents($file));
$this->assertCellGenerated('RecentCell', 'recent');
}

public function testGenerateCellSimpleName(): void
{
command('make:cell Another');

// Check the class was generated
$file = APPPATH . 'Cells/AnotherCell.php';
$this->assertStringContainsString('File created: ' . clean_path($file), $this->getStreamFilterBuffer());
$this->assertFileExists($file);
$this->assertStringContainsString('class AnotherCell extends Cell', $this->getFileContents($file));

// Check the view was generated
$file = APPPATH . 'Cells/another.php';
$this->assertStringContainsString('File created: ' . clean_path($file), $this->getStreamFilterBuffer());
$this->assertFileExists($file);
$this->assertSame("<div>\n <!-- Your HTML here -->\n</div>\n", $this->getFileContents($file));
$this->assertCellGenerated('AnotherCell', 'another');
}

public function testGenerateCellWithCellInBetween(): void
{
command('make:cell PippoCellular');

// Check the class was generated
$file = APPPATH . 'Cells/PippoCellularCell.php';
$this->assertStringContainsString('File created: ' . clean_path($file), $this->getStreamFilterBuffer());
$this->assertFileExists($file);
$this->assertStringContainsString('class PippoCellularCell extends Cell', $this->getFileContents($file));

// Check the view was generated
$file = APPPATH . 'Cells/pippo_cellular.php';
$this->assertStringContainsString('File created: ' . clean_path($file), $this->getStreamFilterBuffer());
$this->assertFileExists($file);
$this->assertSame("<div>\n <!-- Your HTML here -->\n</div>\n", $this->getFileContents($file));
$this->assertCellGenerated('PippoCellularCell', 'pippo_cellular');
}

public function testGenerateCellInSubNamespace(): void
{
command('make:cell admin/stats');

$this->assertCellGenerated('Admin/StatsCell', 'Admin/stats');
$this->assertStringContainsString('namespace App\Cells\Admin;', $this->getContents('Admin/StatsCell.php'));
}

public function testExistingClassStillGeneratesMissingView(): void
{
command('make:cell RecentCell');
unlink(APPPATH . 'Cells/recent.php');
$this->resetStreamFilterBuffer();

command('make:cell RecentCell');

$this->assertSame(
<<<'EOT'
File exists: "APPPATH/Cells/RecentCell.php"
File created: APPPATH/Cells/recent.php

EOT,
$this->getUndecoratedBuffer(),
);
}

public function testForceOverwritesBothFiles(): void
{
command('make:cell RecentCell');
$this->resetStreamFilterBuffer();

command('make:cell RecentCell --force');

$this->assertSame(
<<<'EOT'
File overwritten: "APPPATH/Cells/RecentCell.php"
File overwritten: "APPPATH/Cells/recent.php"

EOT,
$this->getUndecoratedBuffer(),
);
}
}
1 change: 1 addition & 0 deletions user_guide_src/source/changelogs/v4.8.0.rst
Original file line number Diff line number Diff line change
Expand Up @@ -411,6 +411,7 @@ Deprecations
- **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.
- **CLI:** ``Commands::run()`` is now deprecated in favor of ``Commands::runLegacy()`` for legacy ``BaseCommand`` commands, and ``Commands::runCommand()`` for modern ``AbstractCommand`` commands.
- **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.
- **CLI:** The ``CLI.generator.viewName.cell`` language string is deprecated. It was never displayed, since ``make:cell`` only prompts for the class name.
- **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.
- **HTTP:** ``URI::setSilent()`` is now hard deprecated. This method was only previously marked as deprecated. It will now trigger a deprecation notice when used.

Expand Down
4 changes: 2 additions & 2 deletions user_guide_src/source/cli/cli_generators.rst
Original file line number Diff line number Diff line change
Expand Up @@ -64,8 +64,8 @@ Argument:

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

make:command
------------
Expand Down
Loading