Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Bake table with enums mapped. #969

Merged
merged 3 commits into from
Dec 28, 2023
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
2 changes: 1 addition & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
"require": {
"php": ">=8.1",
"brick/varexporter": "^0.4.0",
"cakephp/cakephp": "^5.0.0",
"cakephp/cakephp": "^5.0.3",
"cakephp/twig-view": "^2.0.0",
"nikic/php-parser": "^4.13.2"
},
Expand Down
45 changes: 45 additions & 0 deletions src/Command/ModelCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -1195,6 +1195,7 @@
'validation' => [],
'rulesChecker' => [],
'behaviors' => [],
'enums' => $this->enums($model, $entity, $namespace),
'connection' => $this->connection,
'fileBuilder' => new FileBuilder($io, "{$namespace}\Model\Table", $parsedFile),
];
Expand Down Expand Up @@ -1355,7 +1356,7 @@
$fixture = new FixtureCommand();
$fixtureArgs = new Arguments(
[$className],
['table' => $useTable] + $args->getOptions(),

Check failure on line 1359 in src/Command/ModelCommand.php

View workflow job for this annotation

GitHub Actions / cs-stan / Coding Standard & Static Analysis

InvalidArgument

src/Command/ModelCommand.php:1359:13: InvalidArgument: Argument 2 of Cake\Console\Arguments::__construct expects array<string, bool|null|string>, but non-empty-array<string, bool|list<string>|null|string> provided (see https://psalm.dev/004)
['name']
);
$fixture->execute($fixtureArgs, $io);
Expand All @@ -1377,9 +1378,53 @@
$test = new TestCommand();
$testArgs = new Arguments(
['table', $className],
$args->getOptions(),

Check failure on line 1381 in src/Command/ModelCommand.php

View workflow job for this annotation

GitHub Actions / cs-stan / Coding Standard & Static Analysis

InvalidArgument

src/Command/ModelCommand.php:1381:13: InvalidArgument: Argument 2 of Cake\Console\Arguments::__construct expects array<string, bool|null|string>, but array<string, bool|list<string>|null|string> provided (see https://psalm.dev/004)
['type', 'name']
);
$test->execute($testArgs, $io);
}

/**
* @param \Cake\ORM\Table $table
* @param string $entity
* @param string $namespace
* @return array<string, class-string>
*/
protected function enums(Table $table, string $entity, string $namespace): array
{
$fields = $this->possibleEnumFields($table->getSchema());
$enumClassNamespace = $namespace . '\Model\Enum\\';

$enums = [];
foreach ($fields as $field) {
dereuromark marked this conversation as resolved.
Show resolved Hide resolved
$enumClassName = $enumClassNamespace . $entity . Inflector::camelize($field);
if (!class_exists($enumClassName)) {
continue;
}

$enums[$field] = $enumClassName;
}

return $enums;
}

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

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

$fields[] = $column;
}

return $fields;
}
}
11 changes: 11 additions & 0 deletions templates/bake/Model/table.twig
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,17 @@ class {{ name }}Table extends Table{{ fileBuilder.classBuilder.implements ? ' im
{%- endif %}
{% endif %}

{%- if enums %}

{% endif %}

{%- if enums %}

{%- for name, className in enums %}
$this->getSchema()->setColumnType('{{ name }}', \Cake\Database\Type\EnumType::from(\{{ className }}::class));
{% endfor %}
{% endif %}

{%- if behaviors %}

{% endif %}
Expand Down
19 changes: 18 additions & 1 deletion tests/TestCase/Command/ModelCommandTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -1973,7 +1973,24 @@ public function testBakeTableWithPlugin()
}

/**
* test generation with counter cach
* test generation with enum
*
* @return void
*/
public function testBakeTableWithEnum(): void
{
$this->generatedFile = APP . 'Model/Table/BakeUsersTable.php';

$this->exec('bake model --no-validation --no-test --no-fixture --no-entity BakeUsers');

$this->assertExitCode(CommandInterface::CODE_SUCCESS);
$this->assertFileExists($this->generatedFile);
$result = file_get_contents($this->generatedFile);
$this->assertStringContainsString('$this->getSchema()->setColumnType(\'status\', \Cake\Database\Type\EnumType::from(\Bake\Test\App\Model\Enum\BakeUserStatus::class));', $result);
}

/**
* test generation with counter cache
*
* @return void
*/
Expand Down
116 changes: 116 additions & 0 deletions tests/comparisons/Model/testBakeTableWithEnum.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
<?php
declare(strict_types=1);

namespace Bake\Test\App\Model\Table;

use Bake\Test\App\Model\Enum\BakeUserStatus;
use Cake\Database\Type\EnumType;
use Cake\ORM\Table;
use Cake\Validation\Validator;

/**
* TestBakeArticles Model
*
* @method \Bake\Test\App\Model\Entity\TestBakeArticle newEmptyEntity()
* @method \Bake\Test\App\Model\Entity\TestBakeArticle newEntity(array $data, array $options = [])
* @method array<\Bake\Test\App\Model\Entity\TestBakeArticle> newEntities(array $data, array $options = [])
* @method \Bake\Test\App\Model\Entity\TestBakeArticle get(mixed $primaryKey, array|string $finder = 'all', \Psr\SimpleCache\CacheInterface|string|null $cache = null, \Closure|string|null $cacheKey = null, mixed ...$args)
* @method \Bake\Test\App\Model\Entity\TestBakeArticle findOrCreate($search, ?callable $callback = null, array $options = [])
* @method \Bake\Test\App\Model\Entity\TestBakeArticle patchEntity(\Cake\Datasource\EntityInterface $entity, array $data, array $options = [])
* @method array<\Bake\Test\App\Model\Entity\TestBakeArticle> patchEntities(iterable $entities, array $data, array $options = [])
* @method \Bake\Test\App\Model\Entity\TestBakeArticle|false save(\Cake\Datasource\EntityInterface $entity, array $options = [])
* @method \Bake\Test\App\Model\Entity\TestBakeArticle saveOrFail(\Cake\Datasource\EntityInterface $entity, array $options = [])
* @method iterable<\Bake\Test\App\Model\Entity\TestBakeArticle>|\Cake\Datasource\ResultSetInterface<\Bake\Test\App\Model\Entity\TestBakeArticle>|false saveMany(iterable $entities, array $options = [])
* @method iterable<\Bake\Test\App\Model\Entity\TestBakeArticle>|\Cake\Datasource\ResultSetInterface<\Bake\Test\App\Model\Entity\TestBakeArticle> saveManyOrFail(iterable $entities, array $options = [])
* @method iterable<\Bake\Test\App\Model\Entity\TestBakeArticle>|\Cake\Datasource\ResultSetInterface<\Bake\Test\App\Model\Entity\TestBakeArticle>|false deleteMany(iterable $entities, array $options = [])
* @method iterable<\Bake\Test\App\Model\Entity\TestBakeArticle>|\Cake\Datasource\ResultSetInterface<\Bake\Test\App\Model\Entity\TestBakeArticle> deleteManyOrFail(iterable $entities, array $options = [])
*
* @mixin \Cake\ORM\Behavior\TimestampBehavior
*/
class TestBakeArticlesTable extends Table
{
/**
* Initialize method
*
* @param array<string, mixed> $config The configuration for the Table.
* @return void
*/
public function initialize(array $config): void
{
parent::initialize($config);

$this->setTable('bake_articles');
$this->setDisplayField('title');
$this->setPrimaryKey('id');

$this->addBehavior('Timestamp');

$this->getSchema()->setColumnType('status', EnumType::from(BakeUserStatus::class));

$this->belongsTo('BakeUsers', [
'foreignKey' => 'bake_user_id',
'joinType' => 'INNER',
]);
$this->hasMany('BakeComments', [
'foreignKey' => 'bake_article_id',
]);
$this->belongsToMany('BakeTags', [
'foreignKey' => 'bake_article_id',
'targetForeignKey' => 'bake_tag_id',
'joinTable' => 'bake_articles_bake_tags',
]);
}

/**
* Default validation rules.
*
* @param \Cake\Validation\Validator $validator Validator instance.
* @return \Cake\Validation\Validator
*/
public function validationDefault(Validator $validator): Validator
{
$validator
->numeric('id')
->allowEmptyString('id', 'create');

$validator
->scalar('name')
->maxLength('name', 100, 'Name must be shorter than 100 characters.')
->requirePresence('name', 'create')
->allowEmptyString('name', null, false);

$validator
->nonNegativeInteger('count')
->requirePresence('count', 'create')
->allowEmptyString('count', null, false);

$validator
->greaterThanOrEqual('price', 0)
->requirePresence('price', 'create')
->allowEmptyString('price', null, false);

$validator
->email('email')
->add('email', 'unique', ['rule' => 'validateUnique', 'provider' => 'table'])
->allowEmptyString('email');

$validator
->uploadedFile('image', [
'optional' => true,
'types' => ['image/jpeg'],
])
->allowEmptyFile('image');

return $validator;
}

/**
* Returns the database connection name to use by default.
*
* @return string
*/
public static function defaultConnectionName(): string
{
return 'test';
}
}
3 changes: 2 additions & 1 deletion tests/schema.php
Original file line number Diff line number Diff line change
Expand Up @@ -208,7 +208,7 @@
'body' => 'text',
'rating' => ['type' => 'float', 'unsigned' => true, 'default' => 0.0, 'null' => false],
'score' => ['type' => 'decimal', 'unsigned' => true, 'default' => 0.0, 'null' => false],
'published' => ['type' => 'boolean', 'length' => 1, 'default' => false, 'null' => false],
'published' => ['type' => 'boolean', 'length' => 1, 'default' => false],
'created' => 'datetime',
'updated' => 'datetime',
],
Expand Down Expand Up @@ -377,6 +377,7 @@
'id' => ['type' => 'integer'],
'username' => ['type' => 'string', 'null' => true, 'length' => 255],
'password' => ['type' => 'string', 'null' => true, 'length' => 255],
'status' => ['type' => 'tinyinteger', 'length' => 2, 'default' => null, 'null' => true],
'created' => ['type' => 'timestamp', 'null' => true],
'updated' => ['type' => 'timestamp', 'null' => true],
],
Expand Down
32 changes: 32 additions & 0 deletions tests/test_app/App/Model/Enum/BakeUserStatus.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<?php
declare(strict_types=1);

/**
* CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
* Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
*
* Licensed under The MIT License
* Redistributions of files must retain the above copyright notice
*
* @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
* @since 3.1.0
* @license https://opensource.org/licenses/mit-license.php MIT License
*/
namespace Bake\Test\App\Model\Enum;

use Cake\Database\Type\EnumLabelInterface;
use Cake\Utility\Inflector;

enum BakeUserStatus: int implements EnumLabelInterface
{
case INACTIVE = 0;
case ACTIVE = 1;

/**
* @return string
*/
public function label(): string
{
return Inflector::humanize(mb_strtolower($this->name));
}
}
Loading