Skip to content

Commit

Permalink
Merge pull request #527 from cakephp/migrator-skip
Browse files Browse the repository at this point in the history
Add `skip` option to Migrator::run()
  • Loading branch information
markstory authored Nov 29, 2021
2 parents 432e78c + 1ba3263 commit 4270426
Show file tree
Hide file tree
Showing 3 changed files with 60 additions and 15 deletions.
9 changes: 9 additions & 0 deletions docs/en/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -869,6 +869,15 @@ If you need to run multiple sets of migrations, those can be run as follows::
['plugin' => 'Documents', 'connection' => 'test_docs']
]);

If your database also contains tables that are not managed by your application
like those created by PostGIS, then you can exclude those tables from the drop
& truncate behavior using the ``skip`` option::

$migrator->run(['connection' => 'test', 'skip' => 'postgis*']);

The ``skip`` option accepts a ``fnmatch()`` compatible pattern to exclude tables
from drop & truncate operations.

If you need to see additional debugging output from migrations are being run,
you can enable a ``debug`` level logger.

Expand Down
47 changes: 32 additions & 15 deletions src/TestSuite/Migrator.php
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,11 @@ public function __construct()
* This is useful if all your migrations are located in config/Migrations,
* or in a single directory, or in a single plugin.
*
* For options, {@see \Migrations\Migrations::migrate()}.
* ## Options
*
* - `skip` A list of `fnmatch` compatible table names that should be ignored.
*
* For additional options {@see \Migrations\Migrations::migrate()}.
*
* @param array<string, mixed> $options Migrate options. Connection defaults to `test`.
* @param bool $truncateTables Truncate all tables after running migrations. Defaults to true.
Expand All @@ -56,7 +60,7 @@ public function run(
* Runs multiple sets of migrations.
* This is useful if your migrations are located in multiple sources, plugins or connections.
*
* For options, {@see \Migrations\Migrations::migrate()}.
* For options, {@see \Migrations\Migrator::run()}.
*
* Example:
*
Expand All @@ -83,20 +87,23 @@ public function runMany(
$connectionsList = [];
foreach ($options as $i => $migrationSet) {
$migrationSet += ['connection' => 'test'];
$skip = $migrationSet['skip'] ?? [];
unset($migrationSet['skip']);

$options[$i] = $migrationSet;
$connectionName = $migrationSet['connection'];
if (!in_array($connectionName, $connectionsList)) {
$connectionsList[] = $connectionName;
$connectionsList[] = ['name' => $connectionName, 'skip' => $skip];
}

$migrations = new Migrations();
if (!in_array($connectionName, $connectionsToDrop) && $this->shouldDropTables($migrations, $migrationSet)) {
$connectionsToDrop[] = $connectionName;
$connectionsToDrop[] = ['name' => $connectionName, 'skip' => $skip];
}
}

foreach ($connectionsToDrop as $connectionName) {
$this->dropTables($connectionName);
foreach ($connectionsToDrop as $item) {
$this->dropTables($item['name'], $item['skip']);
}

// Run all sets of migrations
Expand All @@ -112,8 +119,8 @@ public function runMany(

// Truncate all connections if required in parameters
if ($truncateTables) {
foreach ($connectionsList as $connectionName) {
$this->truncate($connectionName);
foreach ($connectionsList as $item) {
$this->truncate($item['name'], $item['skip']);
}
}
}
Expand All @@ -124,16 +131,17 @@ public function runMany(
* For options, {@see \Migrations\Migrations::migrate()}.
*
* @param string $connection Connection name to truncate all non-phinx tables
* @param string[] $skip A fnmatch compatible list of table names to skip.
* @return void
*/
public function truncate(string $connection): void
public function truncate(string $connection, array $skip = []): void
{
// Don't recreate schema if we are in a phpunit separate process test.
if (isset($GLOBALS['__PHPUNIT_BOOTSTRAP'])) {
return;
}

$tables = $this->getNonPhinxTables($connection);
$tables = $this->getNonPhinxTables($connection, $skip);
if ($tables) {
$this->helper->truncateTables($connection, $tables);
}
Expand Down Expand Up @@ -177,11 +185,12 @@ protected function shouldDropTables(Migrations $migrations, array $options): boo
* and truncates the phinx tables.
*
* @param string $connection Connection on which tables are dropped.
* @param string[] $skip A fnmatch compatible list of tables to skip.
* @return void
*/
protected function dropTables(string $connection): void
protected function dropTables(string $connection, array $skip = []): void
{
$dropTables = $this->getNonPhinxTables($connection);
$dropTables = $this->getNonPhinxTables($connection, $skip);
if (count($dropTables)) {
$this->helper->dropTables($connection, $dropTables);
}
Expand Down Expand Up @@ -210,14 +219,22 @@ protected function getPhinxTables(string $connection): array
* Get the list of tables that are not phinxlog related.
*
* @param string $connection The connection name to operate on.
* @param string[] $skip A fnmatch compatible list of tables to skip.
* @return string[] The list of tables that are not related to phinx in the provided connection.
*/
protected function getNonPhinxTables(string $connection): array
protected function getNonPhinxTables(string $connection, array $skip): array
{
$tables = ConnectionManager::get($connection)->getSchemaCollection()->listTables();
$skip[] = '*phinxlog*';

return array_filter($tables, function ($table) {
return strpos($table, 'phinxlog') === false;
return array_filter($tables, function ($table) use ($skip) {
foreach ($skip as $pattern) {
if (fnmatch($pattern, $table) === true) {
return false;
}
}

return true;
});
}
}
19 changes: 19 additions & 0 deletions tests/TestCase/TestSuite/MigratorTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,25 @@ public function testMigrateDropNoTruncate(): void
$this->assertCount(1, $connection->query('SELECT * FROM migrator')->fetchAll());
}

public function testMigrateSkipTables(): void
{
$connection = ConnectionManager::get('test');

// Create a table
$connection->execute('CREATE TABLE skipme (name TEXT)');

// Insert a record so that we can ensure the table was skipped.
$connection->execute('INSERT INTO skipme (name) VALUES (:name)', ['name' => 'Ron']);

$migrator = new Migrator();
$migrator->run(['plugin' => 'Migrator', 'skip' => ['skip*']]);

$tables = $connection->getSchemaCollection()->listTables();
$this->assertContains('migrator', $tables);
$this->assertContains('skipme', $tables);
$this->assertCount(1, $connection->query('SELECT * FROM skipme')->fetchAll());
}

public function testRunManyDropTruncate(): void
{
$migrator = new Migrator();
Expand Down

0 comments on commit 4270426

Please sign in to comment.