Skip to content

Commit 8d27e00

Browse files
authored
Fix upgrade command not matching plugins with slashes (#1039)
* Fix upgrade command not matching plugins with slashes When upgrading from legacy phinxlog tables, the plugin name was being derived by camelizing the table prefix (e.g. cake_d_c_users -> CakeDCUsers). This didn't work for plugins with slashes in their names like CakeDC/Users since the slash was replaced with underscore during table name generation. This fix builds a map of loaded plugin names to their expected table prefixes, allowing proper matching of plugins like CakeDC/Users. Fixes #1038 * Fix test for SQL Server compatibility
1 parent ca1b74e commit 8d27e00

File tree

2 files changed

+94
-1
lines changed

2 files changed

+94
-1
lines changed

src/Command/UpgradeCommand.php

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
use Cake\Console\Arguments;
1818
use Cake\Console\ConsoleIo;
1919
use Cake\Console\ConsoleOptionParser;
20+
use Cake\Core\Plugin;
2021
use Cake\Database\Connection;
2122
use Cake\Database\Exception\QueryException;
2223
use Cake\Datasource\ConnectionManager;
@@ -184,20 +185,51 @@ protected function findLegacyTables(Connection $connection): array
184185
$tables = $schema->listTables();
185186
$legacyTables = [];
186187

188+
// Build a map of expected table prefixes to plugin names for loaded plugins
189+
// This allows matching plugins with special characters like CakeDC/Users
190+
$pluginPrefixMap = $this->buildPluginPrefixMap();
191+
187192
foreach ($tables as $table) {
188193
if ($table === 'phinxlog') {
189194
$legacyTables[$table] = null;
190195
} elseif (str_ends_with($table, '_phinxlog')) {
191196
// Extract plugin name from table name
192197
$prefix = substr($table, 0, -9); // Remove '_phinxlog'
193-
$plugin = Inflector::camelize($prefix);
198+
199+
// Try to match against loaded plugins first
200+
if (isset($pluginPrefixMap[$prefix])) {
201+
$plugin = $pluginPrefixMap[$prefix];
202+
} else {
203+
// Fall back to camelizing the prefix
204+
$plugin = Inflector::camelize($prefix);
205+
}
194206
$legacyTables[$table] = $plugin;
195207
}
196208
}
197209

198210
return $legacyTables;
199211
}
200212

213+
/**
214+
* Build a map of table prefixes to plugin names for all loaded plugins.
215+
*
216+
* This handles plugins with special characters like CakeDC/Users where
217+
* the table prefix is cake_d_c_users but the plugin name is CakeDC/Users.
218+
*
219+
* @return array<string, string> Map of table prefix => plugin name
220+
*/
221+
protected function buildPluginPrefixMap(): array
222+
{
223+
$map = [];
224+
foreach (Plugin::loaded() as $plugin) {
225+
$prefix = Inflector::underscore($plugin);
226+
$prefix = str_replace(['\\', '/', '.'], '_', $prefix);
227+
$map[$prefix] = $plugin;
228+
}
229+
230+
return $map;
231+
}
232+
201233
/**
202234
* Check if a table exists.
203235
*

tests/TestCase/Command/UpgradeCommandTest.php

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,4 +166,65 @@ public function testExecuteWithMigrations(): void
166166

167167
$this->assertCount(1, $rows);
168168
}
169+
170+
/**
171+
* Test that plugins with slashes (like CakeDC/Users) are correctly identified
172+
* during upgrade from legacy phinxlog tables.
173+
*/
174+
public function testExecuteWithSlashInPluginName(): void
175+
{
176+
Configure::write('Migrations.legacyTables', true);
177+
178+
// Create the plugin's phinxlog table using the adapter for cross-database compatibility
179+
$config = ConnectionManager::getConfig('test');
180+
$environment = new Environment('default', [
181+
'connection' => 'test',
182+
'database' => $config['database'],
183+
'migration_table' => 'cake_d_c_users_phinxlog',
184+
]);
185+
$adapter = $environment->getAdapter();
186+
try {
187+
$adapter->createSchemaTable();
188+
} catch (Exception $e) {
189+
// Table probably exists
190+
}
191+
192+
// Insert a migration record
193+
$adapter->getInsertBuilder()
194+
->insert(['version', 'migration_name', 'breakpoint'])
195+
->into('cake_d_c_users_phinxlog')
196+
->values([
197+
'version' => '20250118143003',
198+
'migration_name' => 'SlashPluginMigration',
199+
'breakpoint' => 0,
200+
])
201+
->execute();
202+
203+
// Load a fake plugin with a slash in the name using loadPlugins
204+
// which properly integrates with the console application
205+
$this->loadPlugins(['CakeDC/Users' => ['path' => TMP]]);
206+
207+
try {
208+
$this->exec('migrations upgrade -c test');
209+
$this->assertExitSuccess();
210+
211+
$this->assertOutputContains('cake_d_c_users_phinxlog (CakeDC/Users)');
212+
213+
// Verify the plugin column has the correct value with slash
214+
$rows = $this->getAdapter()->getSelectBuilder()
215+
->select(['version', 'migration_name', 'plugin'])
216+
->from('cake_migrations')
217+
->where(['migration_name' => 'SlashPluginMigration'])
218+
->all();
219+
220+
$this->assertCount(1, $rows);
221+
$this->assertSame('CakeDC/Users', $rows[0]['plugin']);
222+
} finally {
223+
// Cleanup
224+
/** @var \Cake\Database\Connection $connection */
225+
$connection = ConnectionManager::get('test');
226+
$connection->execute('DROP TABLE ' . $connection->getDriver()->quoteIdentifier('cake_d_c_users_phinxlog'));
227+
$this->removePlugins(['CakeDC/Users']);
228+
}
229+
}
169230
}

0 commit comments

Comments
 (0)