Skip to content

Commit

Permalink
Complete baking of enums.
Browse files Browse the repository at this point in the history
  • Loading branch information
dereuromark committed Dec 28, 2023
1 parent b5f106f commit 07520cd
Show file tree
Hide file tree
Showing 5 changed files with 191 additions and 9 deletions.
90 changes: 87 additions & 3 deletions src/Command/EnumCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@

use Cake\Console\Arguments;
use Cake\Console\ConsoleOptionParser;
use Cake\Utility\Inflector;
use InvalidArgumentException;

/**
* Enum code generator.
Expand Down Expand Up @@ -64,8 +66,20 @@ public function template(): string
*/
public function templateData(Arguments $arguments): array
{
$cases = $this->parseCases($arguments->getArgument('cases'), (bool)$arguments->getOption('int'));
$isOfTypeInt = $this->isOfTypeInt($cases);
$backingType = $isOfTypeInt ? 'int' : 'string';
if ($arguments->getOption('int')) {
if ($cases && !$isOfTypeInt) {
throw new InvalidArgumentException('The cases provided do not seem to match the int type you want to bake');

Check warning on line 74 in src/Command/EnumCommand.php

View check run for this annotation

Codecov / codecov/patch

src/Command/EnumCommand.php#L74

Added line #L74 was not covered by tests
}

$backingType = 'int';
}

$data = parent::templateData($arguments);
$data['backingType'] = $arguments->getOption('int') ? 'int' : 'string';
$data['backingType'] = $backingType;
$data['cases'] = $this->formatCases($cases);

return $data;
}
Expand All @@ -82,12 +96,82 @@ public function buildOptionParser(ConsoleOptionParser $parser): ConsoleOptionPar

$parser->setDescription(
'Bake backed enums for use in models.'
)->addOption('int', [
'help' => 'Using backed enums with int instead of string as return type',
)->addArgument('name', [
'help' => 'Name of the enum to bake. You can use Plugin.name to bake plugin enums.',
'required' => true,
])->addArgument('cases', [
'help' => 'List of either `one,two` for string or `0:foo,1:bar` for int type.',
])->addOption('int', [
'help' => 'Using backed enums with int instead of string as return type.',
'boolean' => true,
'short' => 'i',
]);

return $parser;
}

/**
* @param string|null $casesString
* @return array<int|string, string>
*/
protected function parseCases(?string $casesString, bool $int): array
{
if ($casesString === null) {
return [];
}

$enumCases = explode(',', $casesString);

Check warning on line 123 in src/Command/EnumCommand.php

View check run for this annotation

Codecov / codecov/patch

src/Command/EnumCommand.php#L123

Added line #L123 was not covered by tests

$definition = [];
foreach ($enumCases as $k => $enumCase) {
$key = $value = trim($enumCase);
if (str_contains($key, ':')) {
$value = trim(mb_substr($key, strpos($key, ':') + 1));
$key = mb_substr($key, 0, strpos($key, ':'));
} elseif ($int) {
$key = $k;

Check warning on line 132 in src/Command/EnumCommand.php

View check run for this annotation

Codecov / codecov/patch

src/Command/EnumCommand.php#L125-L132

Added lines #L125 - L132 were not covered by tests
}

$definition[$key] = $value;

Check warning on line 135 in src/Command/EnumCommand.php

View check run for this annotation

Codecov / codecov/patch

src/Command/EnumCommand.php#L135

Added line #L135 was not covered by tests
}

return $definition;

Check warning on line 138 in src/Command/EnumCommand.php

View check run for this annotation

Codecov / codecov/patch

src/Command/EnumCommand.php#L138

Added line #L138 was not covered by tests
}

/**
* @param array<int|string, string> $definition
* @return bool
*/
protected function isOfTypeInt(array $definition): bool
{
if (!$definition) {
return false;
}

foreach ($definition as $key => $value) {
if (!is_int($key)) {
return false;

Check warning on line 153 in src/Command/EnumCommand.php

View check run for this annotation

Codecov / codecov/patch

src/Command/EnumCommand.php#L151-L153

Added lines #L151 - L153 were not covered by tests
}
}

return true;

Check warning on line 157 in src/Command/EnumCommand.php

View check run for this annotation

Codecov / codecov/patch

src/Command/EnumCommand.php#L157

Added line #L157 was not covered by tests
}

/**
* @param array<int|string, string> $cases
* @return array<string>
*/
protected function formatCases(array $cases): array
{
$formatted = [];
foreach ($cases as $case => $alias) {
$alias = mb_strtoupper(Inflector::underscore($alias));
if (is_string($case)) {
$case = '\'' . $case . '\'';

Check warning on line 170 in src/Command/EnumCommand.php

View check run for this annotation

Codecov / codecov/patch

src/Command/EnumCommand.php#L168-L170

Added lines #L168 - L170 were not covered by tests
}
$formatted[] = 'case ' . $alias . ' = ' . $case . ';';

Check warning on line 172 in src/Command/EnumCommand.php

View check run for this annotation

Codecov / codecov/patch

src/Command/EnumCommand.php#L172

Added line #L172 was not covered by tests
}

return $formatted;
}
}
100 changes: 97 additions & 3 deletions src/Command/ModelCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,8 @@ public function bake(string $name, Arguments $args, ConsoleIo $io): void
$tableObject = $this->getTableObject($name, $table);
$this->validateNames($tableObject->getSchema(), $io);
$data = $this->getTableContext($tableObject, $table, $name, $args, $io);

$this->bakeEnums($tableObject, $data, $args, $io);
$this->bakeTable($tableObject, $data, $args, $io);
$this->bakeEntity($tableObject, $data, $args, $io);
$this->bakeFixture($tableObject->getAlias(), $tableObject->getTable(), $args, $io);
Expand Down Expand Up @@ -168,6 +170,7 @@ public function getTableContext(
$behaviors = $this->getBehaviors($tableObject);
$connection = $this->connection;
$hidden = $this->getHiddenFields($tableObject, $args);
$enumSchema = $this->getEnumDefinitions($tableObject->getSchema());

return compact(
'associations',
Expand All @@ -181,7 +184,8 @@ public function getTableContext(
'rulesChecker',
'behaviors',
'connection',
'hidden'
'hidden',
'enumSchema',
);
}

Expand Down Expand Up @@ -1118,7 +1122,7 @@ public function getCounterCache(Table $model): array
* Bake an entity class.
*
* @param \Cake\ORM\Table $model Model name or object
* @param array $data An array to use to generate the Table
* @param array<string, mixed> $data An array to use to generate the Table
* @param \Cake\Console\Arguments $args CLI Arguments
* @param \Cake\Console\ConsoleIo $io CLI io
* @return void
Expand Down Expand Up @@ -1170,7 +1174,7 @@ public function bakeEntity(Table $model, array $data, Arguments $args, ConsoleIo
* Bake a table class.
*
* @param \Cake\ORM\Table $model Model name or object
* @param array $data An array to use to generate the Table
* @param array<string, mixed> $data An array to use to generate the Table
* @param \Cake\Console\Arguments $args CLI Arguments
* @param \Cake\Console\ConsoleIo $io CLI Arguments
* @return void
Expand Down Expand Up @@ -1444,4 +1448,94 @@ protected function possibleEnumFields(TableSchemaInterface $schema): array

return $fields;
}

/**
* @param \Cake\Database\Schema\TableSchemaInterface $schema
* @return array<string, mixed>
*/
protected function getEnumDefinitions(TableSchemaInterface $schema): array
{
$enums = [];

foreach ($schema->columns() as $column) {
$columnSchema = $schema->getColumn($column);
if (!in_array($columnSchema['type'], ['string', 'integer', 'tinyinteger', 'smallinteger'], true)) {
continue;
}

if (empty($columnSchema['comment']) || strpos($columnSchema['comment'], '[enum]') === false) {
continue;
}

$enumsDefinitionString = mb_substr($columnSchema['comment'], strpos($columnSchema['comment'], '[enum]') + 6);
$enumsDefinition = $this->parseEnumsDefinition($enumsDefinitionString);
if (!$enumsDefinition) {
continue;

Check warning on line 1473 in src/Command/ModelCommand.php

View check run for this annotation

Codecov / codecov/patch

src/Command/ModelCommand.php#L1470-L1473

Added lines #L1470 - L1473 were not covered by tests
}

$enums[$column] = $enumsDefinition;

Check warning on line 1476 in src/Command/ModelCommand.php

View check run for this annotation

Codecov / codecov/patch

src/Command/ModelCommand.php#L1476

Added line #L1476 was not covered by tests
}

return $enums;
}

/**
* @param string $enumsDefinitionString
* @return array<int|string, string>
*/
protected function parseEnumsDefinition(string $enumsDefinitionString): array

Check warning on line 1486 in src/Command/ModelCommand.php

View check run for this annotation

Codecov / codecov/patch

src/Command/ModelCommand.php#L1486

Added line #L1486 was not covered by tests
{
$enumCases = explode(',', $enumsDefinitionString);

Check warning on line 1488 in src/Command/ModelCommand.php

View check run for this annotation

Codecov / codecov/patch

src/Command/ModelCommand.php#L1488

Added line #L1488 was not covered by tests

$definition = [];
foreach ($enumCases as $enumCase) {
$key = $value = trim($enumCase);
if (str_contains($key, ':')) {
$value = trim(mb_substr($key, strpos($key, ':') + 1));
$key = mb_substr($key, 0, strpos($key, ':'));

Check warning on line 1495 in src/Command/ModelCommand.php

View check run for this annotation

Codecov / codecov/patch

src/Command/ModelCommand.php#L1490-L1495

Added lines #L1490 - L1495 were not covered by tests
}

$definition[$key] = mb_strtolower($value);

Check warning on line 1498 in src/Command/ModelCommand.php

View check run for this annotation

Codecov / codecov/patch

src/Command/ModelCommand.php#L1498

Added line #L1498 was not covered by tests
}

return $definition;

Check warning on line 1501 in src/Command/ModelCommand.php

View check run for this annotation

Codecov / codecov/patch

src/Command/ModelCommand.php#L1501

Added line #L1501 was not covered by tests
}

/**
* @param \Cake\ORM\Table $model
* @param array<string, mixed> $data
* @param \Cake\Console\Arguments $args
* @param \Cake\Console\ConsoleIo $io
* @return void
*/
protected function bakeEnums(Table $model, array $data, Arguments $args, ConsoleIo $io): void
{
$enums = $data['enumSchema'];
if (!$enums) {
return;
}

$entity = $this->_entityName($model->getAlias());

Check warning on line 1518 in src/Command/ModelCommand.php

View check run for this annotation

Codecov / codecov/patch

src/Command/ModelCommand.php#L1518

Added line #L1518 was not covered by tests

foreach ($enums as $column => $enum) {
$enumCommand = new EnumCommand();

Check warning on line 1521 in src/Command/ModelCommand.php

View check run for this annotation

Codecov / codecov/patch

src/Command/ModelCommand.php#L1520-L1521

Added lines #L1520 - L1521 were not covered by tests

$name = $entity . Inflector::camelize($column);
if ($this->plugin) {
$name = $this->plugin . '.' . $name;

Check warning on line 1525 in src/Command/ModelCommand.php

View check run for this annotation

Codecov / codecov/patch

src/Command/ModelCommand.php#L1523-L1525

Added lines #L1523 - L1525 were not covered by tests
}

$cases = [];
foreach ($enum as $k => $v) {
$cases[] = $k . ':' . $v;

Check warning on line 1530 in src/Command/ModelCommand.php

View check run for this annotation

Codecov / codecov/patch

src/Command/ModelCommand.php#L1528-L1530

Added lines #L1528 - L1530 were not covered by tests
}

$args = new Arguments(
[$name, implode(',', $cases)],
['int' => false] + $args->getOptions(),
['name', 'cases']
);
$enumCommand->execute($args, $io);

Check warning on line 1538 in src/Command/ModelCommand.php

View check run for this annotation

Codecov / codecov/patch

src/Command/ModelCommand.php#L1533-L1538

Added lines #L1533 - L1538 were not covered by tests
}
}
}
6 changes: 5 additions & 1 deletion templates/bake/Model/enum.twig
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,15 @@
{{ DocBlock.classDescription(name, 'Enum', [])|raw }}
enum {{ name }}: {{ backingType }} implements EnumLabelInterface
{
{% if cases %}
{{ Bake.concat('\n ', cases) }}

{% endif %}
/**
* @return string
*/
public function label(): string
{
return Inflector::humanize(Inflector::underscore($this->name));
return Inflector::humanize(mb_strtolower($this->name));
}
}
2 changes: 1 addition & 1 deletion tests/comparisons/Model/testBakeEnum.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,6 @@ enum FooBar: string implements EnumLabelInterface
*/
public function label(): string
{
return Inflector::humanize(Inflector::underscore($this->name));
return Inflector::humanize(mb_strtolower($this->name));
}
}
2 changes: 1 addition & 1 deletion tests/comparisons/Model/testBakeEnumBackedInt.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,6 @@ enum FooBar: int implements EnumLabelInterface
*/
public function label(): string
{
return Inflector::humanize(Inflector::underscore($this->name));
return Inflector::humanize(mb_strtolower($this->name));
}
}

0 comments on commit 07520cd

Please sign in to comment.