Skip to content

Commit 9261948

Browse files
Fix up decimal migration_diff (#938)
* Fix up decimal migration_diff * Add .gitkeep for MigrationsDiffDecimalChange directory * Update tests/TestCase/Command/BakeMigrationDiffCommandTest.php * Update tests/TestCase/Command/BakeMigrationDiffCommandTest.php * Fixes. --------- Co-authored-by: Mark Story <mark@mark-story.com>
1 parent 10fd8bf commit 9261948

7 files changed

Lines changed: 244 additions & 9 deletions

File tree

src/Command/BakeMigrationDiffCommand.php

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -300,11 +300,38 @@ protected function getColumns(): void
300300
}
301301
}
302302

303-
if (isset($changedAttributes['length'])) {
303+
// For decimal columns, handle CakePHP schema -> migration attribute mapping
304+
if ($column['type'] === 'decimal') {
305+
// In CakePHP schema: 'length' = precision, 'precision' = scale
306+
// In migrations: 'precision' = precision, 'scale' = scale
307+
308+
// Convert CakePHP schema's 'precision' (which is scale) to migration's 'scale'
309+
if (isset($changedAttributes['precision'])) {
310+
$changedAttributes['scale'] = $changedAttributes['precision'];
311+
unset($changedAttributes['precision']);
312+
}
313+
314+
// Convert CakePHP schema's 'length' (which is precision) to migration's 'precision'
315+
if (isset($changedAttributes['length'])) {
316+
$changedAttributes['precision'] = $changedAttributes['length'];
317+
unset($changedAttributes['length']);
318+
}
319+
320+
// Ensure both precision and scale are always set for decimal columns
321+
if (!isset($changedAttributes['precision']) && isset($column['length'])) {
322+
$changedAttributes['precision'] = $column['length'];
323+
}
324+
if (!isset($changedAttributes['scale']) && isset($column['precision'])) {
325+
$changedAttributes['scale'] = $column['precision'];
326+
}
327+
328+
// Remove 'limit' for decimal columns as they use precision/scale instead
329+
unset($changedAttributes['limit']);
330+
} elseif (isset($changedAttributes['length'])) {
331+
// For non-decimal columns, convert 'length' to 'limit'
304332
if (!isset($changedAttributes['limit'])) {
305333
$changedAttributes['limit'] = $changedAttributes['length'];
306334
}
307-
308335
unset($changedAttributes['length']);
309336
}
310337

src/View/Helper/MigrationHelper.php

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -387,6 +387,7 @@ public function getColumnOption(array $options): array
387387
'comment',
388388
'autoIncrement',
389389
'precision',
390+
'scale',
390391
'after',
391392
'collate',
392393
]);
@@ -420,7 +421,10 @@ public function getColumnOption(array $options): array
420421
unset($columnOptions['precision']);
421422
} else {
422423
// due to Phinx using different naming for the precision and scale to CakePHP
423-
$columnOptions['scale'] = $columnOptions['precision'];
424+
// Only convert precision to scale if scale is not already set (for decimal columns from diff)
425+
if (!isset($columnOptions['scale'])) {
426+
$columnOptions['scale'] = $columnOptions['precision'];
427+
}
424428

425429
if (isset($columnOptions['limit'])) {
426430
$columnOptions['precision'] = $columnOptions['limit'];

tests/TestCase/Command/BakeMigrationDiffCommandTest.php

Lines changed: 108 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,13 @@
1818
use Cake\Core\Configure;
1919
use Cake\Core\Plugin;
2020
use Cake\Database\Driver\Mysql;
21+
use Cake\Database\Driver\Postgres;
22+
use Cake\Database\Driver\Sqlite;
23+
use Cake\Database\Driver\Sqlserver;
2124
use Cake\Datasource\ConnectionManager;
2225
use Cake\TestSuite\StringCompareTrait;
2326
use Cake\Utility\Inflector;
27+
use Exception;
2428
use Migrations\Migrations;
2529
use Migrations\Test\TestCase\TestCase;
2630
use function Cake\Core\env;
@@ -47,6 +51,36 @@ public function setUp(): void
4751
parent::setUp();
4852

4953
$this->generatedFiles = [];
54+
55+
// Clean up any TheDiff migration files from all directories before test starts
56+
$configPath = ROOT . DS . 'config' . DS;
57+
$directories = glob($configPath . '*', GLOB_ONLYDIR) ?: [];
58+
foreach ($directories as $dir) {
59+
// Clean up TheDiff migration files
60+
$migrationFiles = glob($dir . DS . '*TheDiff*.php') ?: [];
61+
foreach ($migrationFiles as $file) {
62+
if (file_exists($file)) {
63+
unlink($file);
64+
}
65+
}
66+
// Clean up Initial migration files
67+
$initialMigrationFiles = glob($dir . DS . '*Initial*.php') ?: [];
68+
foreach ($initialMigrationFiles as $file) {
69+
if (file_exists($file)) {
70+
unlink($file);
71+
}
72+
}
73+
}
74+
75+
// Clean up test_decimal_types table if it exists
76+
if (env('DB_URL_COMPARE')) {
77+
try {
78+
$connection = ConnectionManager::get('test_comparisons');
79+
$connection->execute('DROP TABLE IF EXISTS test_decimal_types');
80+
} catch (Exception $e) {
81+
// Ignore errors if connection doesn't exist yet
82+
}
83+
}
5084
}
5185

5286
public function tearDown(): void
@@ -57,10 +91,29 @@ public function tearDown(): void
5791
unlink($file);
5892
}
5993
}
94+
95+
// Clean up any TheDiff migration files from all directories
96+
$configPath = ROOT . DS . 'config' . DS;
97+
$directories = glob($configPath . '*', GLOB_ONLYDIR) ?: [];
98+
foreach ($directories as $dir) {
99+
$migrationFiles = glob($dir . DS . '*TheDiff*.php') ?: [];
100+
foreach ($migrationFiles as $file) {
101+
if (file_exists($file)) {
102+
unlink($file);
103+
}
104+
}
105+
$initialMigrationFiles = glob($dir . DS . '*Initial*.php') ?: [];
106+
foreach ($initialMigrationFiles as $file) {
107+
if (file_exists($file)) {
108+
unlink($file);
109+
}
110+
}
111+
}
112+
60113
if (env('DB_URL_COMPARE')) {
61114
// Clean up the comparison database each time. Table order is important.
62115
$connection = ConnectionManager::get('test_comparisons');
63-
$tables = ['articles', 'categories', 'comments', 'users', 'orphan_table', 'phinxlog', 'tags', 'test_blog_phinxlog'];
116+
$tables = ['articles', 'categories', 'comments', 'users', 'orphan_table', 'phinxlog', 'tags', 'test_blog_phinxlog', 'test_decimal_types'];
64117
foreach ($tables as $table) {
65118
$connection->execute("DROP TABLE IF EXISTS $table");
66119
}
@@ -240,6 +293,17 @@ public function testBakingDiffWithAutoIdIncompatibleUnsignedPrimaryKeys(): void
240293
$this->runDiffBakingTest('WithAutoIdIncompatibleUnsignedPrimaryKeys');
241294
}
242295

296+
/**
297+
* Tests baking a diff with decimal column changes
298+
* Regression test for issue #659
299+
*/
300+
public function testBakingDiffDecimalChange(): void
301+
{
302+
$this->skipIf(!env('DB_URL_COMPARE'));
303+
304+
$this->runDiffBakingTest('DecimalChange');
305+
}
306+
243307
/**
244308
* Tests that baking a diff with --plugin option only includes tables with Table classes
245309
*/
@@ -330,13 +394,21 @@ protected function runDiffBakingTest(string $scenario): void
330394
{
331395
$this->skipIf(!env('DB_URL_COMPARE'));
332396

397+
// Detect database type from connection if DB env is not set
398+
$db = env('DB') ?: $this->getDbType();
399+
333400
$diffConfigFolder = Plugin::path('Migrations') . 'tests' . DS . 'comparisons' . DS . 'Diff' . DS . lcfirst($scenario) . DS;
334-
$diffMigrationsPath = $diffConfigFolder . 'the_diff_' . Inflector::underscore($scenario) . '_' . env('DB') . '.php';
335-
$diffDumpPath = $diffConfigFolder . 'schema-dump-test_comparisons_' . env('DB') . '.lock';
401+
402+
// DecimalChange uses 'initial_' prefix to avoid class name conflicts
403+
$prefix = $scenario === 'DecimalChange' ? 'initial_' : 'the_diff_';
404+
$classPrefix = $scenario === 'DecimalChange' ? 'Initial' : 'TheDiff';
405+
406+
$diffMigrationsPath = $diffConfigFolder . $prefix . Inflector::underscore($scenario) . '_' . $db . '.php';
407+
$diffDumpPath = $diffConfigFolder . 'schema-dump-test_comparisons_' . $db . '.lock';
336408

337409
$destinationConfigDir = ROOT . DS . 'config' . DS . "MigrationsDiff{$scenario}" . DS;
338-
$destination = $destinationConfigDir . "20160415220805_TheDiff{$scenario}" . ucfirst(env('DB')) . '.php';
339-
$destinationDumpPath = $destinationConfigDir . 'schema-dump-test_comparisons_' . env('DB') . '.lock';
410+
$destination = $destinationConfigDir . "20160415220805_{$classPrefix}{$scenario}" . ucfirst($db) . '.php';
411+
$destinationDumpPath = $destinationConfigDir . 'schema-dump-test_comparisons_' . $db . '.lock';
340412
copy($diffMigrationsPath, $destination);
341413

342414
$this->generatedFiles = [
@@ -387,6 +459,29 @@ protected function runDiffBakingTest(string $scenario): void
387459
$this->getMigrations("MigrationsDiff{$scenario}")->rollback(['target' => 'all']);
388460
}
389461

462+
/**
463+
* Detect database type from connection
464+
*
465+
* @return string Database type (mysql, pgsql, sqlite, sqlserver)
466+
*/
467+
protected function getDbType(): string
468+
{
469+
$connection = ConnectionManager::get('test_comparisons');
470+
$driver = $connection->getDriver();
471+
472+
if ($driver instanceof Mysql) {
473+
return 'mysql';
474+
} elseif ($driver instanceof Postgres) {
475+
return 'pgsql';
476+
} elseif ($driver instanceof Sqlite) {
477+
return 'sqlite';
478+
} elseif ($driver instanceof Sqlserver) {
479+
return 'sqlserver';
480+
}
481+
482+
return 'mysql'; // Default fallback
483+
}
484+
390485
/**
391486
* Get the baked filename based on the current db environment
392487
*
@@ -395,7 +490,11 @@ protected function runDiffBakingTest(string $scenario): void
395490
*/
396491
public function getBakeName($name)
397492
{
398-
$name .= ucfirst(getenv('DB'));
493+
$db = getenv('DB');
494+
if (!$db) {
495+
$db = $this->getDbType();
496+
}
497+
$name .= ucfirst($db);
399498

400499
return $name;
401500
}
@@ -428,6 +527,9 @@ protected function getMigrations($source = 'MigrationsDiff')
428527
public function assertCorrectSnapshot($bakeName, $result)
429528
{
430529
$dbenv = getenv('DB');
530+
if (!$dbenv) {
531+
$dbenv = $this->getDbType();
532+
}
431533
$bakeName = Inflector::underscore($bakeName);
432534
if (file_exists($this->_compareBasePath . $dbenv . DS . $bakeName . '.php')) {
433535
$this->assertSameAsFile($dbenv . DS . $bakeName . '.php', $result);
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
<?php
2+
declare(strict_types=1);
3+
4+
use Migrations\BaseMigration;
5+
6+
class InitialDecimalChangeMysql extends BaseMigration
7+
{
8+
/**
9+
* Up Method.
10+
*
11+
* More information on this method is available here:
12+
* https://book.cakephp.org/migrations/5/en/migrations.html#the-up-method
13+
*
14+
* @return void
15+
*/
16+
public function up(): void
17+
{
18+
$this->table('test_decimal_types')
19+
->addColumn('amount', 'decimal', [
20+
'default' => null,
21+
'null' => false,
22+
'precision' => 5,
23+
'scale' => 2,
24+
])
25+
->create();
26+
}
27+
28+
/**
29+
* Down Method.
30+
*
31+
* More information on this method is available here:
32+
* https://book.cakephp.org/migrations/5/en/migrations.html#the-down-method
33+
*
34+
* @return void
35+
*/
36+
public function down(): void
37+
{
38+
$this->table('test_decimal_types')->drop()->save();
39+
}
40+
}
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
<?php
2+
declare(strict_types=1);
3+
4+
use Migrations\BaseMigration;
5+
6+
class TheDiffDecimalChangeMysql extends BaseMigration
7+
{
8+
/**
9+
* Up Method.
10+
*
11+
* More information on this method is available here:
12+
* https://book.cakephp.org/migrations/5/en/migrations.html#the-up-method
13+
*
14+
* @return void
15+
*/
16+
public function up(): void
17+
{
18+
19+
$this->table('test_decimal_types')
20+
->changeColumn('id', 'integer', [
21+
'default' => null,
22+
'length' => null,
23+
'limit' => null,
24+
'null' => false,
25+
'signed' => false,
26+
])
27+
->changeColumn('amount', 'decimal', [
28+
'default' => null,
29+
'null' => false,
30+
'precision' => 5,
31+
'scale' => 2,
32+
])
33+
->update();
34+
}
35+
36+
/**
37+
* Down Method.
38+
*
39+
* More information on this method is available here:
40+
* https://book.cakephp.org/migrations/5/en/migrations.html#the-down-method
41+
*
42+
* @return void
43+
*/
44+
public function down(): void
45+
{
46+
47+
$this->table('test_decimal_types')
48+
->changeColumn('id', 'integer', [
49+
'autoIncrement' => true,
50+
'default' => null,
51+
'length' => 11,
52+
'null' => false,
53+
])
54+
->changeColumn('amount', 'decimal', [
55+
'default' => null,
56+
'null' => false,
57+
'precision' => 10,
58+
'scale' => 2,
59+
])
60+
->update();
61+
}
62+
}
Binary file not shown.

tests/test_app/config/MigrationsDiffDecimalChange/.gitkeep

Whitespace-only changes.

0 commit comments

Comments
 (0)