Skip to content

Commit

Permalink
Bake table with enums mapped.
Browse files Browse the repository at this point in the history
  • Loading branch information
dereuromark committed Dec 23, 2023
1 parent 82cb1d2 commit 141b704
Show file tree
Hide file tree
Showing 6 changed files with 223 additions and 2 deletions.
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 @@ public function bakeTable(Table $model, array $data, Arguments $args, ConsoleIo
'validation' => [],
'rulesChecker' => [],
'behaviors' => [],
'enums' => $this->enums($model, $entity, $namespace),
'connection' => $this->connection,
'fileBuilder' => new FileBuilder($io, "{$namespace}\Model\Table", $parsedFile),
];
Expand Down Expand Up @@ -1382,4 +1383,48 @@ public function bakeTest(string $className, Arguments $args, ConsoleIo $io): voi
);
$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->enumLikeFields($table->getSchema());
$enumClassNamespace = $namespace . '\Model\Enum\\';

$enums = [];
foreach ($fields as $field) {
$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 enumLikeFields(TableSchemaInterface $schema): array
{
$fields = [];

foreach ($schema->columns() as $column) {
$columnSchema = $schema->getColumn($column);
if (!in_array($columnSchema['type'], ['string', 'tinyinteger'], 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 @@ -1888,7 +1888,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
31 changes: 31 additions & 0 deletions tests/test_app/App/Model/Enum/BakeUserStatus.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
<?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;

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

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

0 comments on commit 141b704

Please sign in to comment.