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
*/