Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add an ability to downgrade migrations by the absolute apply time #2499

Merged
merged 2 commits into from
Feb 20, 2014
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions framework/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,7 @@ Yii Framework 2 Change Log
- Enh #2436: Label of the attribute, which looks like `relatedModel.attribute`, will be received from the related model if it available (djagya)
- Enh #2415: Added support for inverse relations (qiangxue)
- Enh #2490: `yii\db\Query::count()` and other query scalar methods now properly handle queries with GROUP BY clause (qiangxue)
- Enh #2499: Added ability to downgrade migrations by their absolute apply time (resurtm, gorcer)
- Enh: Added support for using arrays as option values for console commands (qiangxue)
- Enh: Added `favicon.ico` and `robots.txt` to default application templates (samdark)
- Enh: Added `Widget::autoIdPrefix` to support prefixing automatically generated widget IDs (qiangxue)
Expand Down
99 changes: 69 additions & 30 deletions framework/console/controllers/MigrateController.php
Original file line number Diff line number Diff line change
Expand Up @@ -286,50 +286,37 @@ public function actionRedo($limit = 1)
/**
* Upgrades or downgrades till the specified version.
*
* Can also downgrade versions to the certain apply time in the past by providing
* a UNIX timestamp or a string parseable by the strtotime() function. This means
* that all the versions applied after the specified certain time would be reverted.
*
* This command will first revert the specified migrations, and then apply
* them again. For example,
*
* ~~~
* yii migrate/to 101129_185401 # using timestamp
* yii migrate/to m101129_185401_create_user_table # using full name
* yii migrate/to 1392853618 # using UNIX timestamp
* yii migrate/to "2014-02-15 13:00:50" # using strtotime() parseable string
* ~~~
*
* @param string $version the version name that the application should be migrated to.
* This can be either the timestamp or the full name of the migration.
* @throws Exception if the version argument is invalid
* @param string $version either the version name or the certain time value in the past
* that the application should be migrated to. This can be either the timestamp,
* the full name of the migration, the UNIX timestamp, or the parseable datetime
* string.
* @throws Exception if the version argument is invalid.
*/
public function actionTo($version)
{
$originalVersion = $version;
if (preg_match('/^m?(\d{6}_\d{6})(_.*?)?$/', $version, $matches)) {
$version = 'm' . $matches[1];
$this->migrateToVersion('m' . $matches[1]);
} elseif ((string)(int)$version == $version) {
$this->migrateToTime($version);
} elseif (($time = strtotime($version)) !== false) {
$this->migrateToTime($time);
} else {
throw new Exception("The version argument must be either a timestamp (e.g. 101129_185401)\nor the full name of a migration (e.g. m101129_185401_create_user_table).");
}

// try migrate up
$migrations = $this->getNewMigrations();
foreach ($migrations as $i => $migration) {
if (strpos($migration, $version . '_') === 0) {
$this->actionUp($i + 1);
return;
}
}

// try migrate down
$migrations = array_keys($this->getMigrationHistory(-1));
foreach ($migrations as $i => $migration) {
if (strpos($migration, $version . '_') === 0) {
if ($i === 0) {
echo "Already at '$originalVersion'. Nothing needs to be done.\n";
} else {
$this->actionDown($i);
}
return;
}
throw new Exception("The version argument must be either a timestamp (e.g. 101129_185401),\n the full name of a migration (e.g. m101129_185401_create_user_table),\n a UNIX timestamp (e.g. 1392853000), or a datetime string parseable\nby the strtotime() function (e.g. 2014-02-15 13:00:50).");
}

throw new Exception("Unable to find the version '$originalVersion'.");
}

/**
Expand Down Expand Up @@ -567,6 +554,58 @@ protected function createMigration($class)
return new $class(['db' => $this->db]);
}

/**
* Migrates to the specified apply time in the past.
* @param integer $time UNIX timestamp value.
*/
protected function migrateToTime($time)
{
$count = 0;
$migrations = array_values($this->getMigrationHistory(-1));
while ($count < count($migrations) && $migrations[$count] > $time) {
++$count;
}
if ($count === 0) {
echo "Nothing needs to be done.\n";
} else {
$this->actionDown($count);
}
}

/**
* Migrates to the certain version.
* @param string $version name in the full format.
* @throws Exception if the provided version cannot be found.
*/
protected function migrateToVersion($version)
{
$originalVersion = $version;

// try migrate up
$migrations = $this->getNewMigrations();
foreach ($migrations as $i => $migration) {
if (strpos($migration, $version . '_') === 0) {
$this->actionUp($i + 1);
return;
}
}

// try migrate down
$migrations = array_keys($this->getMigrationHistory(-1));
foreach ($migrations as $i => $migration) {
if (strpos($migration, $version . '_') === 0) {
if ($i === 0) {
echo "Already at '$originalVersion'. Nothing needs to be done.\n";
} else {
$this->actionDown($i);
}
return;
}
}

throw new Exception("Unable to find the version '$originalVersion'.");
}

/**
* Returns the migration history.
* @param integer $limit the maximum number of records in the history to be returned
Expand Down