diff --git a/docs/guide/db-migrations.md b/docs/guide/db-migrations.md index 9a4111935d..09738a3954 100644 --- a/docs/guide/db-migrations.md +++ b/docs/guide/db-migrations.md @@ -788,6 +788,13 @@ yii migrate/redo 3 # redo the last 3 applied migrations > Note: If a migration is not reversible, you will not be able to redo it. +## Refreshing Migrations + +Since Yii 2.0.13 you can reset the whole database and apply all migrations from the beginning. + +``` +yii migrate/fresh # Truncate the database and apply all migrations from the beginning. +``` ## Listing Migrations diff --git a/framework/CHANGELOG.md b/framework/CHANGELOG.md index 7245fe29d0..077804afed 100644 --- a/framework/CHANGELOG.md +++ b/framework/CHANGELOG.md @@ -15,6 +15,7 @@ Yii Framework 2 Change Log - Bug #14533: Fixed `yii\validators\ExistValidator` and `yii\validators\UniqueValidator` throw exception in case they are set for `yii\db\ActiveRecord` with `$targetClass` pointing to NOSQL ActiveRecord (klimov-paul) - Bug #14449: Fix PHP 7.2 compatibility bugs and add explicit closure support in `yii\base\Application` (dynasource) - Bug #7890: Allow `migrate/mark` to mark history at the point of the base migration (cebe) +- Enh #14664: Add migrate/fresh command to truncate database and apply migrations again (thyseus) - Bug #14206: `MySqlMutex`, `PgsqlMutex` and `OracleMutex` now use `useMaster()` to ensure lock is aquired on the same DB server (cebe, ryusoft) - Chg #14321: `yii\widgets\MaskedInput` is now registering its JavaScript `clientOptions` initialization code in head section (DaveFerger) - Bug #13757: Fixed ambiguous column error in `BaseActiveRecord::refresh()` when the query adds a JOIN by default (cebe, ivankff) diff --git a/framework/console/controllers/BaseMigrateController.php b/framework/console/controllers/BaseMigrateController.php index 7850828930..9c61bf4a31 100644 --- a/framework/console/controllers/BaseMigrateController.php +++ b/framework/console/controllers/BaseMigrateController.php @@ -427,6 +427,30 @@ abstract class BaseMigrateController extends Controller throw new Exception("Unable to find the version '$originalVersion'."); } + /** + * Truncates the whole database and starts the migration from the beginning. + * + * ``` + * yii migrate/fresh + * ``` + * + * @since 2.0.13 + */ + public function actionFresh() + { + if (YII_ENV_PROD) { + $this->stdout("YII_ENV is set to 'prod'.\nRefreshing migrations is not possible on production systems.\n"); + return ExitCode::OK; + } + + if ($this->confirm( + "Are you sure you want to reset the database and start the migration from the beginning?\nAll data will be lost irreversibly!")) { + $this->refreshDatabase(); + } else { + $this->stdout('Action was cancelled by user. Nothing has been performed.'); + } + } + /** * Checks if given migration version specification matches namespaced migration name. * @param string $rawVersion raw version specification received from user input. @@ -877,6 +901,19 @@ abstract class BaseMigrateController extends Controller return $this->renderFile(Yii::getAlias($this->templateFile), $params); } + /** + * Truncates the database and reapplies all migrations from the beginning. + * + * This method will simply print a message in the base class implementation. + * It should be overwritten in subclasses to implement the task of clearing the database. + * + * @since 2.0.13 + */ + protected function refreshDatabase() + { + $this->stdout('This command is not implemented in ' . get_class($this) . "\n"); + } + /** * Returns the migration history. * @param int $limit the maximum number of records in the history to be returned. `null` for "no limit". diff --git a/framework/console/controllers/MigrateController.php b/framework/console/controllers/MigrateController.php index f9ea9875c6..7714530bb1 100644 --- a/framework/console/controllers/MigrateController.php +++ b/framework/console/controllers/MigrateController.php @@ -271,6 +271,35 @@ class MigrateController extends BaseMigrateController ])->execute(); } + /** + * @inheritdoc + * @since 2.0.13 + */ + protected function refreshDatabase() + { + $db = $this->db; + $schemas = $db->schema->getTableSchemas(); + + // First drop all foreign keys, + foreach ($schemas as $schema) { + if ($schema->foreignKeys) { + foreach ($schema->foreignKeys as $name => $foreignKey) { + $db->createCommand()->dropForeignKey($name, $schema->name)->execute(); + $this->stdout("Foreign key $name dropped.\n"); + } + } + } + + // Then drop the tables: + foreach ($schemas as $schema) { + $db->createCommand()->dropTable($schema->name)->execute(); + $this->stdout("Table {$schema->name} dropped.\n"); + } + + // The database should be cleaned up. Start the migrations! + $this->actionUp(); + } + /** * @inheritdoc */ diff --git a/tests/framework/console/controllers/HelpControllerTest.php b/tests/framework/console/controllers/HelpControllerTest.php index 8f92613d00..de91d31554 100644 --- a/tests/framework/console/controllers/HelpControllerTest.php +++ b/tests/framework/console/controllers/HelpControllerTest.php @@ -77,6 +77,7 @@ help/usage migrate migrate/create migrate/down +migrate/fresh migrate/history migrate/mark migrate/new diff --git a/tests/framework/console/controllers/MigrateControllerTest.php b/tests/framework/console/controllers/MigrateControllerTest.php index ef054589ff..be268ae543 100644 --- a/tests/framework/console/controllers/MigrateControllerTest.php +++ b/tests/framework/console/controllers/MigrateControllerTest.php @@ -233,6 +233,28 @@ class MigrateControllerTest extends TestCase } } + /** + * Test the migrate:fresh command. + */ + public function testRefreshMigration() + { + Yii::$app->db->createCommand( + "create table hall_of_fame(id int, string varchar(255))")->execute(); + + Yii::$app->db->createCommand( + "insert into hall_of_fame values(1, 'Qiang Xue');")->execute(); + Yii::$app->db->createCommand( + "insert into hall_of_fame values(2, 'Alexander Makarov');")->execute(); + + $result = $this->runMigrateControllerAction('fresh'); + + // Drop worked + $this->assertContains('Table hall_of_fame dropped.', $result); + + // Migration was restarted + $this->assertContains('No new migrations found. Your system is up-to-date.', $result); + } + /** * @see https://github.com/yiisoft/yii2/issues/12980 */