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
197 changes: 93 additions & 104 deletions system/Commands/Generators/ModelGenerator.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,132 +13,121 @@

namespace CodeIgniter\Commands\Generators;

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

/**
* Generates a skeleton Model file.
*/
class ModelGenerator extends BaseCommand
use CodeIgniter\CLI\Input\Option;

#[Command(name: 'make:model', description: 'Generates a new model file.', group: 'Generators')]
#[GeneratorCommand(
component: 'Model',
template: 'model.tpl.php',
directory: 'Models',
classNameLang: 'CLI.generator.className.model',
)]
class ModelGenerator extends AbstractGeneratorCommand
{
use GeneratorTrait;

/**
* The Command's Group
*
* @var string
*/
protected $group = 'Generators';

/**
* The Command's Name
*
* @var string
*/
protected $name = 'make:model';

/**
* The Command's Description
*
* @var string
*/
protected $description = 'Generates a new model file.';

/**
* The Command's Usage
*
* @var string
*/
protected $usage = 'make:model <name> [options]';
private const RETURN_TYPES = ['array', 'object', 'entity'];

/**
* The Command's Arguments
*
* @var array<string, string>
*/
protected $arguments = [
'name' => 'The model class name.',
];

/**
* The Command's Options
*
* @var array<string, string>
*/
protected $options = [
'--table' => 'Supply a table name. Default: "the lowercased plural of the class name".',
'--dbgroup' => 'Database group to use. Default: "default".',
'--return' => 'Return type, Options: [array, object, entity]. Default: "array".',
'--namespace' => 'Set root namespace. Default: "APP_NAMESPACE".',
'--suffix' => 'Append the component title to the class name (e.g. User => UserModel).',
'--force' => 'Force overwrite existing file.',
];
protected function configure(): void
{
parent::configure();

$this
->addOption(new Option(
name: 'table',
shortcut: 't',
description: 'Table name. Defaults to the lowercased plural of the class name.',
acceptsValue: true,
valueLabel: 'name',
))
->addOption(new Option(
name: 'dbgroup',
shortcut: 'g',
description: 'Database group to use.',
acceptsValue: true,
valueLabel: 'group',
))
->addOption(new Option(
name: 'return',
shortcut: 'r',
description: 'Return type: "array", "object", or "entity".',
requiresValue: true,
valueLabel: 'type',
default: 'array',
));
}

/**
* Actually execute a command.
*/
public function run(array $params)
protected function interact(array &$arguments, array &$options): void
{
$this->component = 'Model';
$this->directory = 'Models';
$this->template = 'model.tpl.php';
$return = $this->getUnboundOption('return', $options);

$this->classNameLang = 'CLI.generator.className.model';
$this->generateClass($params);
if (! is_string($return) || in_array($return, self::RETURN_TYPES, true)) {
return;
}

return EXIT_SUCCESS;
$options['return'] = CLI::prompt(lang('CLI.generator.returnType'), self::RETURN_TYPES, 'required');
}

/**
* Prepare options and do the necessary replacements.
*/
protected function prepare(string $class): string
protected function execute(array $arguments, array $options): int
{
$table = $this->getOption('table');
$dbGroup = $this->getOption('dbgroup');
$return = $this->getOption('return');
$return = $this->getValidatedOption('return');

$baseClass = class_basename($class);
if (! in_array($return, self::RETURN_TYPES, true)) {
CLI::error(lang('CLI.generator.invalidReturnType', [$return]));

if (preg_match('/^(\S+)Model$/i', $baseClass, $match) === 1) {
$baseClass = $match[1];
return EXIT_ERROR;
}

$table = is_string($table) ? $table : plural(strtolower($baseClass));
$return = is_string($return) ? $return : 'array';
$exitCode = $this->generateClass();

if (! in_array($return, ['array', 'object', 'entity'], true)) {
// @codeCoverageIgnoreStart
$return = CLI::prompt(lang('CLI.generator.returnType'), ['array', 'object', 'entity'], 'required');
CLI::newLine();
// @codeCoverageIgnoreEnd
if ($exitCode !== EXIT_SUCCESS || $return !== 'entity') {
return $exitCode;
}

if ($return === 'entity') {
// Build the fully-qualified entity class from the model class so
// that the generated Entity keeps any sub-namespaces (eg. Admin).
$entityClass = str_replace('Models', 'Entities', $class);
$entityOptions = ['namespace' => $this->getValidatedOption('namespace')];

if (preg_match('/^(\S+)Model$/i', $entityClass, $match) === 1) {
$entityClass = $match[1];
if ($this->getValidatedOption('force') === true) {
$entityOptions['force'] = null;
}

return $this->call('make:entity', [$this->getEntityClass($this->qualifyClassName())], $entityOptions);
}

if ($this->getOption('suffix')) {
$entityClass .= 'Entity';
}
}
protected function getReplacements(string $class): array
{
$table = $this->getValidatedOption('table');
$dbGroup = $this->getValidatedOption('dbgroup');

$return = $this->getValidatedOption('return') === 'entity'
? '\\' . $this->getEntityClass($class) . '::class'
: sprintf("'%s'", $this->getValidatedOption('return'));

return [
'{dbGroup}' => is_string($dbGroup) ? $dbGroup : '',
'{table}' => is_string($table) ? $table : plural(strtolower($this->stripModelSuffix(class_basename($class)))),
'{return}' => $return,
];
}

// Call the entity generator with the fully-qualified class name so
// it ends up under the correct sub-namespace/folder (eg. Admin).
$entityOptions = array_intersect_key($this->params, array_flip(['namespace', 'suffix', 'force']));
protected function getTemplateData(string $class): array
{
return ['dbGroup' => $this->getValidatedOption('dbgroup')];
}

$this->call('make:entity', array_merge([trim($entityClass, '\\')], $entityOptions));
/**
* Derives the entity class from the qualified model class, keeping any sub-namespace.
*/
private function getEntityClass(string $class): string
{
$entity = $this->stripModelSuffix(str_replace('\\Models\\', '\\Entities\\', $class));

$return = '\\' . trim($entityClass, '\\') . '::class';
} else {
$return = "'{$return}'";
}
return $this->shouldAppendSuffix() ? $entity . 'Entity' : $entity;
}

return $this->parseTemplate($class, ['{dbGroup}', '{table}', '{return}'], [$dbGroup, $table, $return], compact('dbGroup'));
private function stripModelSuffix(string $class): string
{
return preg_replace('/^(.+)Model$/i', '$1', $class) ?? $class;
}
}
4 changes: 2 additions & 2 deletions system/Commands/Generators/ScaffoldGenerator.php
Original file line number Diff line number Diff line change
Expand Up @@ -108,11 +108,11 @@ public function run(array $params)
$controllerOpts['restful'] = is_string($restful) ? $restful : null;
}

$modelOpts = [
$modelOpts = array_filter([
'table' => $this->getOption('table'),
'dbgroup' => $this->getOption('dbgroup'),
'return' => $this->getOption('return'),
];
], is_string(...));

$class = $params[0] ?? CLI::getSegment(2);

Expand Down
1 change: 1 addition & 0 deletions system/Language/en/CLI.php
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
'fileOverwrite' => 'File overwritten: "{0}"',
'invalidClassName' => 'Class name "{0}" is not valid.',
'invalidParentClass' => 'Parent class "{0}" is not valid.',
'invalidReturnType' => 'Return type "{0}" is not valid.',
'parentClass' => 'Parent class',
'returnType' => 'Return type',
'tableName' => 'Table name',
Expand Down
Loading
Loading