Skip to content

Commit a0c3d9e

Browse files
committed
Add support for generating enum/set columns
Add shim logic to extend support for enum and set column types. There have been a few issues opened about the removal of enum columns from migrations. With these changes migrations can once again generate create table, add column and change column operations for enum columns. Reflection of enum columns and values has not been added. Fixes #907 Fixes #895
1 parent 9b8e228 commit a0c3d9e

3 files changed

Lines changed: 78 additions & 4 deletions

File tree

src/Db/Adapter/MysqlAdapter.php

Lines changed: 36 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
use Cake\Core\Configure;
1212
use Cake\Database\Connection;
1313
use Cake\Database\Exception\QueryException;
14+
use Cake\Database\Schema\SchemaDialect;
1415
use Cake\Database\Schema\TableSchema;
1516
use InvalidArgumentException;
1617
use Migrations\Db\AlterInstructions;
@@ -231,8 +232,7 @@ public function createTable(Table $table, array $columns = [], array $indexes =
231232
$sql = 'CREATE TABLE ';
232233
$sql .= $this->quoteTableName($table->getName()) . ' (';
233234
foreach ($columns as $column) {
234-
$columnData = $this->mapColumnData($column->toArray());
235-
$sql .= $dialect->columnDefinitionSql($columnData) . ', ';
235+
$sql .= $this->columnDefinitionSql($dialect, $column) . ', ';
236236
}
237237

238238
// set the primary key(s)
@@ -329,6 +329,38 @@ protected function mapColumnData(array $data): array
329329
return $data;
330330
}
331331

332+
/**
333+
* Get the SQL fragment for a column definition.
334+
*
335+
* This method provides backwards compatibility for enum and set types
336+
* as userland migrations use those types, but they are not supported
337+
* in cakephp/database.
338+
*
339+
* @param \Cake\Database\Schema\SchemaDialect $dialect The dialect to use.
340+
* @param \Migrations\Db\Table\Column $column The column to get the SQL for.
341+
* @return string
342+
*/
343+
protected function columnDefinitionSql(SchemaDialect $dialect, Column $column): string
344+
{
345+
$columnData = $column->toArray();
346+
$deprecatedTypes = [self::PHINX_TYPE_ENUM, self::PHINX_TYPE_SET];
347+
if (in_array($columnData['type'], $deprecatedTypes, true)) {
348+
$sql = $this->quoteColumnName($columnData['name']) . ' ' . $columnData['type'];
349+
$values = $column->getValues();
350+
if ($values && is_array($values)) {
351+
$sql .= '(' . implode(', ', array_map(function ($value) {
352+
// Special case NULL to trigger errors as it isn't allowed
353+
// in enum values.
354+
return $value === null ? 'NULL' : $this->quoteString($value);
355+
}, $values)) . ')';
356+
}
357+
358+
return $sql;
359+
}
360+
361+
return $dialect->columnDefinitionSql($this->mapColumnData($columnData));
362+
}
363+
332364
/**
333365
* {@inheritDoc}
334366
*
@@ -502,7 +534,7 @@ protected function getAddColumnInstructions(Table $table, Column $column): Alter
502534
$dialect = $this->getSchemaDialect();
503535
$alter = sprintf(
504536
'ADD %s',
505-
$dialect->columnDefinitionSql($this->mapColumnData($column->toArray())),
537+
$this->columnDefinitionSql($dialect, $column),
506538
);
507539

508540
$alter .= $this->afterClause($column);
@@ -585,7 +617,7 @@ protected function getChangeColumnInstructions(string $tableName, string $column
585617
$alter = sprintf(
586618
'CHANGE %s %s%s',
587619
$this->quoteColumnName($columnName),
588-
$dialect->columnDefinitionSql($this->mapColumnData($newColumn->toArray())),
620+
$this->columnDefinitionSql($dialect, $newColumn),
589621
$this->afterClause($newColumn),
590622
);
591623

src/Db/Table/Column.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -852,6 +852,7 @@ public function toArray(): array
852852
'timezone' => $this->getTimezone(),
853853
'comment' => $this->getComment(),
854854
'autoIncrement' => $this->getIdentity(),
855+
'values' => $this->getValues(),
855856
];
856857
}
857858
}

tests/TestCase/Db/Adapter/MysqlAdapterTest.php

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -496,6 +496,18 @@ public function testCreateTableWithUnsignedNamedPK()
496496
$this->assertFalse($this->adapter->hasColumn('ntable', 'address'));
497497
}
498498

499+
public function testCreateTableWithSetEnumTypes()
500+
{
501+
$table = new Table('enum_test', [], $this->adapter);
502+
$table->addColumn('status', 'enum', ['values' => ['pending', 'active', 'archived']])
503+
->addColumn('kind', 'set', ['values' => ['a', 'b']])
504+
->save();
505+
506+
$this->assertTrue($this->adapter->hasTable('enum_test'));
507+
$this->assertTrue($this->adapter->hasColumn('enum_test', 'status'));
508+
$this->assertTrue($this->adapter->hasColumn('enum_test', 'kind'));
509+
}
510+
499511
#[RunInSeparateProcess]
500512
public function testUnsignedPksFeatureFlag()
501513
{
@@ -968,6 +980,21 @@ public function testChangeColumnDefaultToNull()
968980
$this->assertNull($rows[1]['Default']);
969981
}
970982

983+
public function testChangeColumnEnum()
984+
{
985+
$table = new Table('t', [], $this->adapter);
986+
$table->addColumn('column1', 'string')
987+
->save();
988+
$this->assertTrue($this->adapter->hasColumn('t', 'column1'));
989+
990+
$table->changeColumn('column1', 'enum', ['values' => ['a', 'b']])->save();
991+
$this->assertTrue($this->adapter->hasColumn('t', 'column1'));
992+
993+
$rows = $this->adapter->fetchAll('SHOW COLUMNS FROM t');
994+
$this->assertNull($rows[1]['Default']);
995+
$this->assertEquals("enum('a','b')", $rows[1]['Type']);
996+
}
997+
971998
public static function sqlTypeIntConversionProvider()
972999
{
9731000
return [
@@ -1915,6 +1942,20 @@ public function testAddColumnWithComment()
19151942
$this->assertEquals($comment, $columnWithComment['COLUMN_COMMENT'], "Didn't set column comment correctly");
19161943
}
19171944

1945+
public function testAddColumnEnum()
1946+
{
1947+
$table = new Table('t', [], $this->adapter);
1948+
$table->addColumn('column1', 'string')
1949+
->save();
1950+
$this->assertTrue($this->adapter->hasColumn('t', 'column1'));
1951+
1952+
$table->addColumn('column2', 'enum', ['values' => ['a', 'b']])->save();
1953+
$this->assertTrue($this->adapter->hasColumn('t', 'column2'));
1954+
1955+
$rows = $this->adapter->fetchAll('SHOW COLUMNS FROM t');
1956+
$this->assertEquals("enum('a','b')", $rows[2]['Type']);
1957+
}
1958+
19181959
public function testAddGeoSpatialColumns()
19191960
{
19201961
$table = new Table('table1', [], $this->adapter);

0 commit comments

Comments
 (0)