diff --git a/src/Command/UpgradeCommand.php b/src/Command/UpgradeCommand.php index 5897c458..c87507e9 100644 --- a/src/Command/UpgradeCommand.php +++ b/src/Command/UpgradeCommand.php @@ -17,6 +17,7 @@ use Cake\Console\Arguments; use Cake\Console\ConsoleIo; use Cake\Console\ConsoleOptionParser; +use Cake\Core\Plugin; use Cake\Database\Connection; use Cake\Database\Exception\QueryException; use Cake\Datasource\ConnectionManager; @@ -181,13 +182,24 @@ protected function findLegacyTables(Connection $connection): array $tables = $schema->listTables(); $legacyTables = []; + // Build a map of expected table prefixes to plugin names for loaded plugins + // This allows matching plugins with special characters like CakeDC/Users + $pluginPrefixMap = $this->buildPluginPrefixMap(); + foreach ($tables as $table) { if ($table === 'phinxlog') { $legacyTables[$table] = null; } elseif (str_ends_with($table, '_phinxlog')) { // Extract plugin name from table name $prefix = substr($table, 0, -9); // Remove '_phinxlog' - $plugin = Inflector::camelize($prefix); + + // Try to match against loaded plugins first + if (isset($pluginPrefixMap[$prefix])) { + $plugin = $pluginPrefixMap[$prefix]; + } else { + // Fall back to camelizing the prefix + $plugin = Inflector::camelize($prefix); + } $legacyTables[$table] = $plugin; } } @@ -195,6 +207,26 @@ protected function findLegacyTables(Connection $connection): array return $legacyTables; } + /** + * Build a map of table prefixes to plugin names for all loaded plugins. + * + * This handles plugins with special characters like CakeDC/Users where + * the table prefix is cake_d_c_users but the plugin name is CakeDC/Users. + * + * @return array Map of table prefix => plugin name + */ + protected function buildPluginPrefixMap(): array + { + $map = []; + foreach (Plugin::loaded() as $plugin) { + $prefix = Inflector::underscore($plugin); + $prefix = str_replace(['\\', '/', '.'], '_', $prefix); + $map[$prefix] = $plugin; + } + + return $map; + } + /** * Check if a table exists. * diff --git a/tests/TestCase/Command/UpgradeCommandTest.php b/tests/TestCase/Command/UpgradeCommandTest.php index ceddd953..c7900bd7 100644 --- a/tests/TestCase/Command/UpgradeCommandTest.php +++ b/tests/TestCase/Command/UpgradeCommandTest.php @@ -166,4 +166,65 @@ public function testExecuteWithMigrations(): void $this->assertCount(1, $rows); } + + /** + * Test that plugins with slashes (like CakeDC/Users) are correctly identified + * during upgrade from legacy phinxlog tables. + */ + public function testExecuteWithSlashInPluginName(): void + { + Configure::write('Migrations.legacyTables', true); + + // Create the plugin's phinxlog table using the adapter for cross-database compatibility + $config = ConnectionManager::getConfig('test'); + $environment = new Environment('default', [ + 'connection' => 'test', + 'database' => $config['database'], + 'migration_table' => 'cake_d_c_users_phinxlog', + ]); + $adapter = $environment->getAdapter(); + try { + $adapter->createSchemaTable(); + } catch (Exception $e) { + // Table probably exists + } + + // Insert a migration record + $adapter->getInsertBuilder() + ->insert(['version', 'migration_name', 'breakpoint']) + ->into('cake_d_c_users_phinxlog') + ->values([ + 'version' => '20250118143003', + 'migration_name' => 'SlashPluginMigration', + 'breakpoint' => 0, + ]) + ->execute(); + + // Load a fake plugin with a slash in the name using loadPlugins + // which properly integrates with the console application + $this->loadPlugins(['CakeDC/Users' => ['path' => TMP]]); + + try { + $this->exec('migrations upgrade -c test'); + $this->assertExitSuccess(); + + $this->assertOutputContains('cake_d_c_users_phinxlog (CakeDC/Users)'); + + // Verify the plugin column has the correct value with slash + $rows = $this->getAdapter()->getSelectBuilder() + ->select(['version', 'migration_name', 'plugin']) + ->from('cake_migrations') + ->where(['migration_name' => 'SlashPluginMigration']) + ->all(); + + $this->assertCount(1, $rows); + $this->assertSame('CakeDC/Users', $rows[0]['plugin']); + } finally { + // Cleanup + /** @var \Cake\Database\Connection $connection */ + $connection = ConnectionManager::get('test'); + $connection->execute('DROP TABLE ' . $connection->getDriver()->quoteIdentifier('cake_d_c_users_phinxlog')); + $this->removePlugins(['CakeDC/Users']); + } + } }