Skip to content

Commit ff9a32f

Browse files
committed
Bake table with enums mapped.
1 parent 237035a commit ff9a32f

File tree

7 files changed

+224
-3
lines changed

7 files changed

+224
-3
lines changed

composer.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
"require": {
2323
"php": ">=8.1",
2424
"brick/varexporter": "^0.4.0",
25-
"cakephp/cakephp": "^5.0.0",
25+
"cakephp/cakephp": "^5.0.3",
2626
"cakephp/twig-view": "^2.0.0",
2727
"nikic/php-parser": "^4.13.2"
2828
},

src/Command/ModelCommand.php

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1195,6 +1195,7 @@ public function bakeTable(Table $model, array $data, Arguments $args, ConsoleIo
11951195
'validation' => [],
11961196
'rulesChecker' => [],
11971197
'behaviors' => [],
1198+
'enums' => $this->enums($model, $entity, $namespace),
11981199
'connection' => $this->connection,
11991200
'fileBuilder' => new FileBuilder($io, "{$namespace}\Model\Table", $parsedFile),
12001201
];
@@ -1382,4 +1383,48 @@ public function bakeTest(string $className, Arguments $args, ConsoleIo $io): voi
13821383
);
13831384
$test->execute($testArgs, $io);
13841385
}
1386+
1387+
/**
1388+
* @param \Cake\ORM\Table $table
1389+
* @param string $entity
1390+
* @param string $namespace
1391+
* @return array<string, class-string>
1392+
*/
1393+
protected function enums(Table $table, string $entity, string $namespace): array
1394+
{
1395+
$fields = $this->possibleEnumFields($table->getSchema());
1396+
$enumClassNamespace = $namespace . '\Model\Enum\\';
1397+
1398+
$enums = [];
1399+
foreach ($fields as $field) {
1400+
$enumClassName = $enumClassNamespace . $entity . Inflector::camelize($field);
1401+
if (!class_exists($enumClassName)) {
1402+
continue;
1403+
}
1404+
1405+
$enums[$field] = $enumClassName;
1406+
}
1407+
1408+
return $enums;
1409+
}
1410+
1411+
/**
1412+
* @param \Cake\Database\Schema\TableSchemaInterface $schema
1413+
* @return array<string>
1414+
*/
1415+
protected function possibleEnumFields(TableSchemaInterface $schema): array
1416+
{
1417+
$fields = [];
1418+
1419+
foreach ($schema->columns() as $column) {
1420+
$columnSchema = $schema->getColumn($column);
1421+
if (!in_array($columnSchema['type'], ['string', 'integer', 'tinyinteger'], true)) {
1422+
continue;
1423+
}
1424+
1425+
$fields[] = $column;
1426+
}
1427+
1428+
return $fields;
1429+
}
13851430
}

templates/bake/Model/table.twig

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,17 @@ class {{ name }}Table extends Table{{ fileBuilder.classBuilder.implements ? ' im
6161
{%- endif %}
6262
{% endif %}
6363

64+
{%- if enums %}
65+
66+
{% endif %}
67+
68+
{%- if enums %}
69+
70+
{%- for name, className in enums %}
71+
$this->getSchema()->setColumnType('{{ name }}', \Cake\Database\Type\EnumType::from(\{{ className }}::class));
72+
{% endfor %}
73+
{% endif %}
74+
6475
{%- if behaviors %}
6576

6677
{% endif %}

tests/TestCase/Command/ModelCommandTest.php

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1973,7 +1973,24 @@ public function testBakeTableWithPlugin()
19731973
}
19741974

19751975
/**
1976-
* test generation with counter cach
1976+
* test generation with enum
1977+
*
1978+
* @return void
1979+
*/
1980+
public function testBakeTableWithEnum(): void
1981+
{
1982+
$this->generatedFile = APP . 'Model/Table/BakeUsersTable.php';
1983+
1984+
$this->exec('bake model --no-validation --no-test --no-fixture --no-entity BakeUsers');
1985+
1986+
$this->assertExitCode(CommandInterface::CODE_SUCCESS);
1987+
$this->assertFileExists($this->generatedFile);
1988+
$result = file_get_contents($this->generatedFile);
1989+
$this->assertStringContainsString('$this->getSchema()->setColumnType(\'status\', \Cake\Database\Type\EnumType::from(\Bake\Test\App\Model\Enum\BakeUserStatus::class));', $result);
1990+
}
1991+
1992+
/**
1993+
* test generation with counter cache
19771994
*
19781995
* @return void
19791996
*/
Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
<?php
2+
declare(strict_types=1);
3+
4+
namespace Bake\Test\App\Model\Table;
5+
6+
use Bake\Test\App\Model\Enum\BakeUserStatus;
7+
use Cake\Database\Type\EnumType;
8+
use Cake\ORM\Table;
9+
use Cake\Validation\Validator;
10+
11+
/**
12+
* TestBakeArticles Model
13+
*
14+
* @method \Bake\Test\App\Model\Entity\TestBakeArticle newEmptyEntity()
15+
* @method \Bake\Test\App\Model\Entity\TestBakeArticle newEntity(array $data, array $options = [])
16+
* @method array<\Bake\Test\App\Model\Entity\TestBakeArticle> newEntities(array $data, array $options = [])
17+
* @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)
18+
* @method \Bake\Test\App\Model\Entity\TestBakeArticle findOrCreate($search, ?callable $callback = null, array $options = [])
19+
* @method \Bake\Test\App\Model\Entity\TestBakeArticle patchEntity(\Cake\Datasource\EntityInterface $entity, array $data, array $options = [])
20+
* @method array<\Bake\Test\App\Model\Entity\TestBakeArticle> patchEntities(iterable $entities, array $data, array $options = [])
21+
* @method \Bake\Test\App\Model\Entity\TestBakeArticle|false save(\Cake\Datasource\EntityInterface $entity, array $options = [])
22+
* @method \Bake\Test\App\Model\Entity\TestBakeArticle saveOrFail(\Cake\Datasource\EntityInterface $entity, array $options = [])
23+
* @method iterable<\Bake\Test\App\Model\Entity\TestBakeArticle>|\Cake\Datasource\ResultSetInterface<\Bake\Test\App\Model\Entity\TestBakeArticle>|false saveMany(iterable $entities, array $options = [])
24+
* @method iterable<\Bake\Test\App\Model\Entity\TestBakeArticle>|\Cake\Datasource\ResultSetInterface<\Bake\Test\App\Model\Entity\TestBakeArticle> saveManyOrFail(iterable $entities, array $options = [])
25+
* @method iterable<\Bake\Test\App\Model\Entity\TestBakeArticle>|\Cake\Datasource\ResultSetInterface<\Bake\Test\App\Model\Entity\TestBakeArticle>|false deleteMany(iterable $entities, array $options = [])
26+
* @method iterable<\Bake\Test\App\Model\Entity\TestBakeArticle>|\Cake\Datasource\ResultSetInterface<\Bake\Test\App\Model\Entity\TestBakeArticle> deleteManyOrFail(iterable $entities, array $options = [])
27+
*
28+
* @mixin \Cake\ORM\Behavior\TimestampBehavior
29+
*/
30+
class TestBakeArticlesTable extends Table
31+
{
32+
/**
33+
* Initialize method
34+
*
35+
* @param array<string, mixed> $config The configuration for the Table.
36+
* @return void
37+
*/
38+
public function initialize(array $config): void
39+
{
40+
parent::initialize($config);
41+
42+
$this->setTable('bake_articles');
43+
$this->setDisplayField('title');
44+
$this->setPrimaryKey('id');
45+
46+
$this->addBehavior('Timestamp');
47+
48+
$this->getSchema()->setColumnType('status', EnumType::from(BakeUserStatus::class));
49+
50+
$this->belongsTo('BakeUsers', [
51+
'foreignKey' => 'bake_user_id',
52+
'joinType' => 'INNER',
53+
]);
54+
$this->hasMany('BakeComments', [
55+
'foreignKey' => 'bake_article_id',
56+
]);
57+
$this->belongsToMany('BakeTags', [
58+
'foreignKey' => 'bake_article_id',
59+
'targetForeignKey' => 'bake_tag_id',
60+
'joinTable' => 'bake_articles_bake_tags',
61+
]);
62+
}
63+
64+
/**
65+
* Default validation rules.
66+
*
67+
* @param \Cake\Validation\Validator $validator Validator instance.
68+
* @return \Cake\Validation\Validator
69+
*/
70+
public function validationDefault(Validator $validator): Validator
71+
{
72+
$validator
73+
->numeric('id')
74+
->allowEmptyString('id', 'create');
75+
76+
$validator
77+
->scalar('name')
78+
->maxLength('name', 100, 'Name must be shorter than 100 characters.')
79+
->requirePresence('name', 'create')
80+
->allowEmptyString('name', null, false);
81+
82+
$validator
83+
->nonNegativeInteger('count')
84+
->requirePresence('count', 'create')
85+
->allowEmptyString('count', null, false);
86+
87+
$validator
88+
->greaterThanOrEqual('price', 0)
89+
->requirePresence('price', 'create')
90+
->allowEmptyString('price', null, false);
91+
92+
$validator
93+
->email('email')
94+
->add('email', 'unique', ['rule' => 'validateUnique', 'provider' => 'table'])
95+
->allowEmptyString('email');
96+
97+
$validator
98+
->uploadedFile('image', [
99+
'optional' => true,
100+
'types' => ['image/jpeg'],
101+
])
102+
->allowEmptyFile('image');
103+
104+
return $validator;
105+
}
106+
107+
/**
108+
* Returns the database connection name to use by default.
109+
*
110+
* @return string
111+
*/
112+
public static function defaultConnectionName(): string
113+
{
114+
return 'test';
115+
}
116+
}

tests/schema.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -208,7 +208,7 @@
208208
'body' => 'text',
209209
'rating' => ['type' => 'float', 'unsigned' => true, 'default' => 0.0, 'null' => false],
210210
'score' => ['type' => 'decimal', 'unsigned' => true, 'default' => 0.0, 'null' => false],
211-
'published' => ['type' => 'boolean', 'length' => 1, 'default' => false, 'null' => false],
211+
'published' => ['type' => 'boolean', 'length' => 1, 'default' => false],
212212
'created' => 'datetime',
213213
'updated' => 'datetime',
214214
],
@@ -377,6 +377,7 @@
377377
'id' => ['type' => 'integer'],
378378
'username' => ['type' => 'string', 'null' => true, 'length' => 255],
379379
'password' => ['type' => 'string', 'null' => true, 'length' => 255],
380+
'status' => ['type' => 'tinyinteger', 'length' => 2, 'default' => null, 'null' => true],
380381
'created' => ['type' => 'timestamp', 'null' => true],
381382
'updated' => ['type' => 'timestamp', 'null' => true],
382383
],
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
<?php
2+
declare(strict_types=1);
3+
4+
/**
5+
* CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
6+
* Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
7+
*
8+
* Licensed under The MIT License
9+
* Redistributions of files must retain the above copyright notice
10+
*
11+
* @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
12+
* @since 3.1.0
13+
* @license https://opensource.org/licenses/mit-license.php MIT License
14+
*/
15+
namespace Bake\Test\App\Model\Enum;
16+
17+
use Cake\Database\Type\EnumLabelInterface;
18+
19+
enum BakeUserStatus: int implements EnumLabelInterface
20+
{
21+
case ACTIVE = 1;
22+
case INACTIVE = 0;
23+
24+
/**
25+
* @return string
26+
*/
27+
public function label(): string
28+
{
29+
return mb_strtolower($this->name);
30+
}
31+
}

0 commit comments

Comments
 (0)