From 57e2c5e4ccaf5c607ca5e228f6cd5ea2ccc07e27 Mon Sep 17 00:00:00 2001 From: mscherer Date: Fri, 8 Aug 2025 15:53:57 +0200 Subject: [PATCH 1/2] Add file name changes. --- src/Command/BakeMigrationCommand.php | 34 ++++++++ src/Command/BakeSimpleMigrationCommand.php | 29 +++++++ src/Migration/Manager.php | 73 +++++++++++++++--- src/Util/Util.php | 29 ++++++- templates/bake/config/skeleton-anonymous.twig | 77 +++++++++++++++++++ .../Command/BakeMigrationCommandTest.php | 35 +++++++++ tests/TestCase/Migration/ManagerTest.php | 20 +++++ tests/TestCase/Util/UtilTest.php | 33 ++++++++ ..._12_08_150000_CreateTestAnonymousTable.php | 30 ++++++++ 9 files changed, 349 insertions(+), 11 deletions(-) create mode 100644 templates/bake/config/skeleton-anonymous.twig create mode 100644 tests/test_app/config/AnonymousMigrations/2024_12_08_150000_CreateTestAnonymousTable.php diff --git a/src/Command/BakeMigrationCommand.php b/src/Command/BakeMigrationCommand.php index 0c3cf082..89eb1eb3 100644 --- a/src/Command/BakeMigrationCommand.php +++ b/src/Command/BakeMigrationCommand.php @@ -60,6 +60,11 @@ public function bake(string $name, Arguments $args, ConsoleIo $io): void */ public function template(): string { + $style = $this->args->getOption('style') ?? Configure::read('Migrations.style', 'traditional'); + if ($style === 'anonymous') { + return 'Migrations.config/skeleton-anonymous'; + } + return 'Migrations.config/skeleton'; } @@ -196,6 +201,19 @@ public function getOptionParser(): ConsoleOptionParser Create a migration that adds (name VARCHAR(128) and a UNIQUE<.warning index) to the projects table. +Migration Styles + +You can generate migrations in different styles: + +bin/cake bake migration --style=anonymous CreatePosts +Creates an anonymous class migration with readable file naming (2024_12_08_120000_CreatePosts.php) + +bin/cake bake migration --style=traditional CreatePosts +Creates a traditional class-based migration (20241208120000_create_posts.php) + +You can set the default style in your configuration: +Configure::write('Migrations.style', 'anonymous'); + TEXT; $parser->setDescription($text); @@ -203,6 +221,22 @@ public function getOptionParser(): ConsoleOptionParser return $parser; } + /** + * @inheritDoc + */ + public function buildOptionParser(ConsoleOptionParser $parser): ConsoleOptionParser + { + $parser = parent::buildOptionParser($parser); + + $parser->addOption('style', [ + 'help' => 'Migration style to use (traditional or anonymous).', + 'default' => 'traditional', + 'choices' => ['traditional', 'anonymous'], + ]); + + return $parser; + } + /** * Detects the action and table from the name of a migration * diff --git a/src/Command/BakeSimpleMigrationCommand.php b/src/Command/BakeSimpleMigrationCommand.php index 8ed82d76..622b61fc 100644 --- a/src/Command/BakeSimpleMigrationCommand.php +++ b/src/Command/BakeSimpleMigrationCommand.php @@ -20,8 +20,11 @@ use Cake\Console\Arguments; use Cake\Console\ConsoleIo; use Cake\Console\ConsoleOptionParser; +use Cake\Core\Configure; use Cake\Core\Plugin; use Cake\Utility\Inflector; +use DateTime; +use DateTimeZone; use Migrations\Util\Util; /** @@ -74,6 +77,32 @@ public function name(): string public function fileName($name): string { $name = $this->getMigrationName($name); + $style = $this->args->getOption('style') ?? Configure::read('Migrations.style', 'traditional'); + + if ($style === 'anonymous') { + // Use readable format: 2024_12_08_120000_CamelCaseName.php + $timestamp = Util::getCurrentTimestamp(); + $dt = new DateTime(); + $dt->setTimestamp((int)substr($timestamp, 0, 10)); + $dt->setTimezone(new DateTimeZone('UTC')); + + $readableDate = $dt->format('Y_m_d'); + $time = substr($timestamp, 8); + $camelName = Inflector::camelize($name); + + $path = $this->getPath($this->args); + $offset = 0; + while (glob($path . $readableDate . '_' . $time . '_*.php')) { + $timestamp = Util::getCurrentTimestamp(++$offset); + $dt->setTimestamp((int)substr($timestamp, 0, 10)); + $readableDate = $dt->format('Y_m_d'); + $time = substr($timestamp, 8); + } + + return $readableDate . '_' . $time . '_' . $camelName . '.php'; + } + + // Traditional format $timestamp = Util::getCurrentTimestamp(); $suffix = '_' . Inflector::camelize($name) . '.php'; diff --git a/src/Migration/Manager.php b/src/Migration/Manager.php index 7a4d33e5..9efcc3c4 100644 --- a/src/Migration/Manager.php +++ b/src/Migration/Manager.php @@ -227,9 +227,37 @@ public function markMigrated(int $version, string $path): bool $migrationFile = $migrationFile[0]; $className = $this->getMigrationClassName($migrationFile); - require_once $migrationFile; - $migration = new $className($version); + // For anonymous classes, we need to use require instead of require_once + $migrationInstance = null; + if (!class_exists($className)) { + $migrationInstance = require $migrationFile; + } else { + require_once $migrationFile; + } + + // Check if the file returns an anonymous class instance or a closure + if (is_callable($migrationInstance)) { + // It's a closure that creates the migration with version + $migration = $migrationInstance($version); + if (!($migration instanceof MigrationInterface)) { + throw new InvalidArgumentException(sprintf( + 'The closure in file "%s" must return an instance of MigrationInterface', + $migrationFile, + )); + } + } elseif (is_object($migrationInstance) && $migrationInstance instanceof MigrationInterface) { + // Direct instance (legacy support) + $migration = $migrationInstance; + $migration->setVersion($version); + } elseif (class_exists($className)) { + $migration = new $className($version); + } else { + throw new RuntimeException( + sprintf('Could not find class `%s` in file `%s` and file did not return a migration instance', $className, $migrationFile), + ); + } + /** @var \Migrations\MigrationInterface $migration */ $config = $this->getConfig(); $migration->setConfig($config); @@ -850,19 +878,47 @@ function ($phpFile) { // load the migration file $orig_display_errors_setting = ini_get('display_errors'); ini_set('display_errors', 'On'); - /** @noinspection PhpIncludeInspection */ - require_once $filePath; - ini_set('display_errors', $orig_display_errors_setting); + + // For anonymous classes, we need to use require instead of require_once + // to get the returned instance + $migrationInstance = null; if (!class_exists($class)) { + $migrationInstance = require $filePath; + } else { + require_once $filePath; + } + + ini_set('display_errors', $orig_display_errors_setting); + + // Check if the file returns an anonymous class instance or a closure + if (is_callable($migrationInstance)) { + // It's a closure that creates the migration with version + $migration = $migrationInstance($version); + if ($migration instanceof MigrationInterface) { + $io->verbose("Using anonymous class from $filePath."); + } else { + throw new InvalidArgumentException(sprintf( + 'The closure in file "%s" must return an instance of MigrationInterface', + $filePath, + )); + } + } elseif (is_object($migrationInstance) && $migrationInstance instanceof MigrationInterface) { + // Direct instance (legacy support) + $io->verbose("Using anonymous class from $filePath."); + $migration = $migrationInstance; + $migration->setVersion($version); + } elseif (class_exists($class)) { + // Fall back to traditional class-based migration + $io->verbose("Constructing $class."); + $migration = new $class($version); + } else { throw new InvalidArgumentException(sprintf( - 'Could not find class `%s` in file `%s`', + 'Could not find class `%s` in file `%s` and file did not return a callable or migration instance', $class, $filePath, )); } - $io->verbose("Constructing $class."); - $migration = new $class($version); /** @var \Migrations\MigrationInterface $migration */ $config = $this->getConfig(); $migration->setConfig($config); @@ -976,7 +1032,6 @@ public function getSeeds(): array $fileNames[$class] = basename($filePath); // load the seed file - /** @noinspection PhpIncludeInspection */ require_once $filePath; if (!class_exists($class)) { throw new InvalidArgumentException(sprintf( diff --git a/src/Util/Util.php b/src/Util/Util.php index a0435b41..c14a30e3 100644 --- a/src/Util/Util.php +++ b/src/Util/Util.php @@ -37,6 +37,15 @@ class Util */ protected const MIGRATION_FILE_NAME_NO_NAME_PATTERN = '/^[0-9]{14}\.php$/'; + /** + * Enhanced migration file name pattern with readable timestamp and CamelCase + * Example: 2024_12_08_120000_CreateUsersTable.php + * + * @var string + * @phpstan-var non-empty-string + */ + protected const READABLE_MIGRATION_FILE_NAME_PATTERN = '/^(\d{4})_(\d{2})_(\d{2})_(\d{6})_([A-Z][a-zA-Z\d]*)\.php$/'; + /** * @var string * @phpstan-var non-empty-string @@ -95,7 +104,16 @@ public static function getExistingMigrationClassNames(string $path): array public static function getVersionFromFileName(string $fileName): int { $matches = []; - preg_match('/^[0-9]+/', basename($fileName), $matches); + $baseName = basename($fileName); + + // Check for readable format: 2024_12_08_120000_CreateUsersTable.php + if (preg_match(static::READABLE_MIGRATION_FILE_NAME_PATTERN, $baseName, $matches)) { + // Convert to standard format: 20241208120000 + return (int)($matches[1] . $matches[2] . $matches[3] . $matches[4]); + } + + // Standard format + preg_match('/^[0-9]+/', $baseName, $matches); $value = (int)($matches[0] ?? null); if (!$value) { throw new RuntimeException(sprintf('Cannot get a valid version from filename `%s`', $fileName)); @@ -135,6 +153,12 @@ public static function mapClassNameToFileName(string $className): string public static function mapFileNameToClassName(string $fileName): string { $matches = []; + + // Check for readable format first: 2024_12_08_120000_CreateUsersTable.php + if (preg_match(static::READABLE_MIGRATION_FILE_NAME_PATTERN, $fileName, $matches)) { + return $matches[5]; // Return the CamelCase class name directly + } + if (preg_match(static::MIGRATION_FILE_NAME_PATTERN, $fileName, $matches)) { $fileName = $matches[1]; } elseif (preg_match(static::MIGRATION_FILE_NAME_NO_NAME_PATTERN, $fileName)) { @@ -153,7 +177,8 @@ public static function mapFileNameToClassName(string $fileName): string public static function isValidMigrationFileName(string $fileName): bool { return (bool)preg_match(static::MIGRATION_FILE_NAME_PATTERN, $fileName) - || (bool)preg_match(static::MIGRATION_FILE_NAME_NO_NAME_PATTERN, $fileName); + || (bool)preg_match(static::MIGRATION_FILE_NAME_NO_NAME_PATTERN, $fileName) + || (bool)preg_match(static::READABLE_MIGRATION_FILE_NAME_PATTERN, $fileName); } /** diff --git a/templates/bake/config/skeleton-anonymous.twig b/templates/bake/config/skeleton-anonymous.twig new file mode 100644 index 00000000..efcfc5e7 --- /dev/null +++ b/templates/bake/config/skeleton-anonymous.twig @@ -0,0 +1,77 @@ +{# +/** + * CakePHP(tm) : Rapid Development Framework (https://cakephp.org) + * Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org) + * + * Licensed under The MIT License + * For full copyright and license information, please see the LICENSE.txt + * Redistributions of files must retain the above copyright notice. + * + * @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org) + * @link https://cakephp.org CakePHP(tm) Project + * @since 3.0.0 + * @license https://www.opensource.org/licenses/mit-license.php MIT License + */ +#} +{% set wantedOptions = {'length': '', 'limit': '', 'default': '', 'unsigned': '', 'null': '', 'comment': '', 'autoIncrement': '', 'precision': '', 'scale': ''} %} +{% set tableMethod = Migration.tableMethod(action) %} +{% set columnMethod = Migration.columnMethod(action) %} +{% set indexMethod = Migration.indexMethod(action) %} +table('{{ table }}'); +{% if tableMethod != 'drop' %} +{% if columnMethod == 'removeColumn' %} +{% for column, config in columns['fields'] %} + $table->{{ columnMethod }}('{{ column }}'); +{% endfor %} +{% for column, config in columns['indexes'] %} + $table->{{ indexMethod }}([{{ + Migration.stringifyList(config['columns']) | raw + }}]); +{% endfor %} +{% else %} +{% for column, config in columns['fields'] %} + $table->{{ columnMethod }}('{{ column }}', '{{ config['columnType'] }}', [{{ + Migration.stringifyList(config['options'], {'indent': 3}, wantedOptions) | raw + }}]); +{% endfor %} +{% for column, config in columns['indexes'] %} + $table->{{ indexMethod }}([{{ + Migration.stringifyList(config['columns'], {'indent': 3}) | raw }} + ], [{{ + Migration.stringifyList(config['options'], {'indent': 3}) | raw + }}]); +{% endfor %} +{% if tableMethod == 'create' and columns['primaryKey'] is not empty %} + $table->addPrimaryKey([{{ + Migration.stringifyList(columns['primaryKey'], {'indent': 3}) | raw + }}]); +{% endif %} +{% endif %} +{% endif %} + $table->{{ tableMethod }}(){% if tableMethod == 'drop' %}->save(){% endif %}; +{% endfor %} + } + }; +}; \ No newline at end of file diff --git a/tests/TestCase/Command/BakeMigrationCommandTest.php b/tests/TestCase/Command/BakeMigrationCommandTest.php index 1cd994f0..b1a26347 100644 --- a/tests/TestCase/Command/BakeMigrationCommandTest.php +++ b/tests/TestCase/Command/BakeMigrationCommandTest.php @@ -49,6 +49,14 @@ public function tearDown(): void } } + // Also clean up readable format files + $files = glob(ROOT . DS . 'config' . DS . 'Migrations' . DS . '????_??_??_??????_*Users.php'); + if ($files) { + foreach ($files as $file) { + unlink($file); + } + } + $files = glob(ROOT . DS . 'config' . DS . 'Migrations' . DS . '*_PrefixNew.php'); if ($files) { foreach ($files as $file) { @@ -384,6 +392,33 @@ public function testActionWithoutValidPrefix() $this->assertErrorContains('When applying fields the migration name should start with one of the following prefixes: `Create`, `Drop`, `Add`, `Remove`, `Alter`.'); } + /** + * Test creating migrations with anonymous style + * + * @return void + */ + public function testCreateAnonymousStyle() + { + $this->exec('bake migration CreateUsers name:string --style=anonymous --connection test'); + + $files = glob(ROOT . DS . 'config' . DS . 'Migrations' . DS . '????_??_??_??????_CreateUsers.php'); + $this->assertCount(1, $files); + + $filePath = current($files); + $fileName = basename($filePath); + + // Check the file name format + $this->assertMatchesRegularExpression('/^\d{4}_\d{2}_\d{2}_\d{6}_CreateUsers\.php$/', $fileName); + + $this->assertExitCode(BaseCommand::CODE_SUCCESS); + $result = file_get_contents($filePath); + + // Check that it returns a closure that creates an anonymous class + $this->assertStringContainsString('return function (int $version)', $result); + $this->assertStringContainsString('return new class($version) extends BaseMigration', $result); + $this->assertStringNotContainsString('class CreateUsers extends', $result); + } + public function testBakeMigrationWithoutBake() { // Make sure to unload the Bake plugin diff --git a/tests/TestCase/Migration/ManagerTest.php b/tests/TestCase/Migration/ManagerTest.php index 42d806c5..731aa02a 100644 --- a/tests/TestCase/Migration/ManagerTest.php +++ b/tests/TestCase/Migration/ManagerTest.php @@ -635,6 +635,26 @@ public function testGetMigrationsWithInvalidMigrationClassName() $manager->getMigrations(); } + public function testGetMigrationsWithAnonymousClass() + { + $config = new Config(['paths' => ['migrations' => ROOT . '/config/AnonymousMigrations']]); + $manager = new Manager($config, $this->io); + + $migrations = $manager->getMigrations(); + + // Should have one migration + $this->assertCount(1, $migrations); + + // Get the migration + $migration = reset($migrations); + + // Check that it's a valid migration object + $this->assertInstanceOf('\Migrations\MigrationInterface', $migration); + + // Check the version was set correctly (2024_12_08_150000 => 20241208150000) + $this->assertEquals(20241208150000, $migration->getVersion()); + } + public function testGettingAValidEnvironment() { $this->assertInstanceOf( diff --git a/tests/TestCase/Util/UtilTest.php b/tests/TestCase/Util/UtilTest.php index 31ef22b3..c017c764 100644 --- a/tests/TestCase/Util/UtilTest.php +++ b/tests/TestCase/Util/UtilTest.php @@ -57,6 +57,13 @@ public function testGetVersionFromFileName(): void $this->assertSame(20221130101652, Util::getVersionFromFileName('20221130101652_test.php')); } + public function testGetVersionFromReadableFileName(): void + { + // Test readable format: 2024_12_08_120000_CreateUsersTable.php + $this->assertSame(20241208120000, Util::getVersionFromFileName('2024_12_08_120000_CreateUsersTable.php')); + $this->assertSame(20231225235959, Util::getVersionFromFileName('2023_12_25_235959_AddFieldToProducts.php')); + } + public function testGetVersionFromFileNameErrorNoVersion(): void { $this->expectException(RuntimeException::class); @@ -102,6 +109,14 @@ public function testMapFileNameToClassName(string $fileName, string $className) $this->assertEquals($className, Util::mapFileNameToClassName($fileName)); } + public function testMapReadableFileNameToClassName(): void + { + // Test readable format: 2024_12_08_120000_CreateUsersTable.php + $this->assertEquals('CreateUsersTable', Util::mapFileNameToClassName('2024_12_08_120000_CreateUsersTable.php')); + $this->assertEquals('AddFieldToProducts', Util::mapFileNameToClassName('2023_12_25_235959_AddFieldToProducts.php')); + $this->assertEquals('DropOrdersTable', Util::mapFileNameToClassName('2024_01_01_000000_DropOrdersTable.php')); + } + public function testGlobPath() { $files = Util::glob(__DIR__ . '/_files/migrations/empty.txt'); @@ -143,4 +158,22 @@ public function testGetFiles() $this->assertEquals('not_a_migration.php', basename($files[2])); $this->assertEquals('foobar.php', basename($files[3])); } + + public function testIsValidMigrationFileName(): void + { + // Traditional format + $this->assertTrue(Util::isValidMigrationFileName('20221130101652_create_users_table.php')); + $this->assertTrue(Util::isValidMigrationFileName('20120111235330_test_migration.php')); + + // No name format + $this->assertTrue(Util::isValidMigrationFileName('20221130101652.php')); + + // Readable format + $this->assertTrue(Util::isValidMigrationFileName('2024_12_08_120000_CreateUsersTable.php')); + $this->assertTrue(Util::isValidMigrationFileName('2023_12_25_235959_AddFieldToProducts.php')); + + // Invalid formats + $this->assertFalse(Util::isValidMigrationFileName('not_a_migration.php')); + $this->assertFalse(Util::isValidMigrationFileName('2024_12_08_120000_camelCaseShouldStartWithCapital.php')); + } } diff --git a/tests/test_app/config/AnonymousMigrations/2024_12_08_150000_CreateTestAnonymousTable.php b/tests/test_app/config/AnonymousMigrations/2024_12_08_150000_CreateTestAnonymousTable.php new file mode 100644 index 00000000..4463c6fa --- /dev/null +++ b/tests/test_app/config/AnonymousMigrations/2024_12_08_150000_CreateTestAnonymousTable.php @@ -0,0 +1,30 @@ +table('test_anonymous'); + $table->addColumn('name', 'string', [ + 'default' => null, + 'limit' => 255, + 'null' => false, + ]); + $table->addColumn('created', 'datetime', [ + 'default' => null, + 'null' => false, + ]); + $table->create(); + } + }; +}; \ No newline at end of file From 785c015c2e0fceaa3c8c694501c2414421c91109 Mon Sep 17 00:00:00 2001 From: mscherer Date: Fri, 8 Aug 2025 17:14:11 +0200 Subject: [PATCH 2/2] Simplify anon classes. --- src/BaseMigration.php | 10 +++-- src/Migration/Manager.php | 32 +++----------- templates/bake/config/skeleton-anonymous.twig | 6 +-- .../Command/BakeMigrationCommandTest.php | 6 +-- ..._12_08_150000_CreateTestAnonymousTable.php | 44 +++++++++---------- 5 files changed, 37 insertions(+), 61 deletions(-) diff --git a/src/BaseMigration.php b/src/BaseMigration.php index 9172b1ef..b5df6c62 100644 --- a/src/BaseMigration.php +++ b/src/BaseMigration.php @@ -85,12 +85,14 @@ class BaseMigration implements MigrationInterface /** * Constructor * - * @param int $version The version this migration is + * @param int|null $version The version this migration is (null for anonymous migrations) */ - public function __construct(int $version) + public function __construct(?int $version = null) { - $this->validateVersion($version); - $this->version = $version; + if ($version !== null) { + $this->validateVersion($version); + $this->version = $version; + } } /** diff --git a/src/Migration/Manager.php b/src/Migration/Manager.php index 9efcc3c4..b2d067eb 100644 --- a/src/Migration/Manager.php +++ b/src/Migration/Manager.php @@ -236,18 +236,8 @@ public function markMigrated(int $version, string $path): bool require_once $migrationFile; } - // Check if the file returns an anonymous class instance or a closure - if (is_callable($migrationInstance)) { - // It's a closure that creates the migration with version - $migration = $migrationInstance($version); - if (!($migration instanceof MigrationInterface)) { - throw new InvalidArgumentException(sprintf( - 'The closure in file "%s" must return an instance of MigrationInterface', - $migrationFile, - )); - } - } elseif (is_object($migrationInstance) && $migrationInstance instanceof MigrationInterface) { - // Direct instance (legacy support) + // Check if the file returns an anonymous class instance + if (is_object($migrationInstance) && $migrationInstance instanceof MigrationInterface) { $migration = $migrationInstance; $migration->setVersion($version); } elseif (class_exists($className)) { @@ -890,20 +880,8 @@ function ($phpFile) { ini_set('display_errors', $orig_display_errors_setting); - // Check if the file returns an anonymous class instance or a closure - if (is_callable($migrationInstance)) { - // It's a closure that creates the migration with version - $migration = $migrationInstance($version); - if ($migration instanceof MigrationInterface) { - $io->verbose("Using anonymous class from $filePath."); - } else { - throw new InvalidArgumentException(sprintf( - 'The closure in file "%s" must return an instance of MigrationInterface', - $filePath, - )); - } - } elseif (is_object($migrationInstance) && $migrationInstance instanceof MigrationInterface) { - // Direct instance (legacy support) + // Check if the file returns an anonymous class instance + if (is_object($migrationInstance) && $migrationInstance instanceof MigrationInterface) { $io->verbose("Using anonymous class from $filePath."); $migration = $migrationInstance; $migration->setVersion($version); @@ -913,7 +891,7 @@ function ($phpFile) { $migration = new $class($version); } else { throw new InvalidArgumentException(sprintf( - 'Could not find class `%s` in file `%s` and file did not return a callable or migration instance', + 'Could not find class `%s` in file `%s` and file did not return a migration instance', $class, $filePath, )); diff --git a/templates/bake/config/skeleton-anonymous.twig b/templates/bake/config/skeleton-anonymous.twig index efcfc5e7..a96bd86e 100644 --- a/templates/bake/config/skeleton-anonymous.twig +++ b/templates/bake/config/skeleton-anonymous.twig @@ -22,9 +22,8 @@ declare(strict_types=1); use Migrations\BaseMigration; -return function (int $version) { - return new class($version) extends BaseMigration - { +return new class extends BaseMigration +{ {% if tableMethod == 'create' and columns['primaryKey'] %} public bool $autoId = false; @@ -73,5 +72,4 @@ return function (int $version) { $table->{{ tableMethod }}(){% if tableMethod == 'drop' %}->save(){% endif %}; {% endfor %} } - }; }; \ No newline at end of file diff --git a/tests/TestCase/Command/BakeMigrationCommandTest.php b/tests/TestCase/Command/BakeMigrationCommandTest.php index b1a26347..b9e6c74e 100644 --- a/tests/TestCase/Command/BakeMigrationCommandTest.php +++ b/tests/TestCase/Command/BakeMigrationCommandTest.php @@ -413,10 +413,10 @@ public function testCreateAnonymousStyle() $this->assertExitCode(BaseCommand::CODE_SUCCESS); $result = file_get_contents($filePath); - // Check that it returns a closure that creates an anonymous class - $this->assertStringContainsString('return function (int $version)', $result); - $this->assertStringContainsString('return new class($version) extends BaseMigration', $result); + // Check that it returns an anonymous class directly + $this->assertStringContainsString('return new class extends BaseMigration', $result); $this->assertStringNotContainsString('class CreateUsers extends', $result); + $this->assertStringNotContainsString('function (int $version)', $result); } public function testBakeMigrationWithoutBake() diff --git a/tests/test_app/config/AnonymousMigrations/2024_12_08_150000_CreateTestAnonymousTable.php b/tests/test_app/config/AnonymousMigrations/2024_12_08_150000_CreateTestAnonymousTable.php index 4463c6fa..3f139819 100644 --- a/tests/test_app/config/AnonymousMigrations/2024_12_08_150000_CreateTestAnonymousTable.php +++ b/tests/test_app/config/AnonymousMigrations/2024_12_08_150000_CreateTestAnonymousTable.php @@ -3,28 +3,26 @@ use Migrations\BaseMigration; -return function (int $version) { - return new class($version) extends BaseMigration +return new class extends BaseMigration +{ + /** + * Change Method. + * + * More information on this method is available here: + * https://book.cakephp.org/migrations/5/en/migrations.html#the-change-method + */ + public function change(): void { - /** - * Change Method. - * - * More information on this method is available here: - * https://book.cakephp.org/migrations/5/en/migrations.html#the-change-method - */ - public function change(): void - { - $table = $this->table('test_anonymous'); - $table->addColumn('name', 'string', [ - 'default' => null, - 'limit' => 255, - 'null' => false, - ]); - $table->addColumn('created', 'datetime', [ - 'default' => null, - 'null' => false, - ]); - $table->create(); - } - }; + $table = $this->table('test_anonymous'); + $table->addColumn('name', 'string', [ + 'default' => null, + 'limit' => 255, + 'null' => false, + ]); + $table->addColumn('created', 'datetime', [ + 'default' => null, + 'null' => false, + ]); + $table->create(); + } }; \ No newline at end of file