mirror of
https://github.com/yiisoft/yii2.git
synced 2025-08-14 06:11:35 +08:00
Add migration namespace support (#12511)
namespace support added to `BaseMigrateController`
This commit is contained in:
@ -36,6 +36,9 @@ Yii Framework 2 Change Log
|
|||||||
- Bug #11541: Fixed default MySQL integer display width for unsigned primary key (h311ion, rob006, cebe)
|
- Bug #11541: Fixed default MySQL integer display width for unsigned primary key (h311ion, rob006, cebe)
|
||||||
- Bug #12143: Fixed `yii\db\BaseActiveRecord::updateAttributes()` change `isNewRecord` state for the new model (klimov-paul)
|
- Bug #12143: Fixed `yii\db\BaseActiveRecord::updateAttributes()` change `isNewRecord` state for the new model (klimov-paul)
|
||||||
- Bug #12463: Fixed `yii\web\Request::getBodyParams()` does not pass full 'content-type' value to `yii\web\RequestParserInterface::parse()` (klimov-paul)
|
- Bug #12463: Fixed `yii\web\Request::getBodyParams()` does not pass full 'content-type' value to `yii\web\RequestParserInterface::parse()` (klimov-paul)
|
||||||
|
- Enh #384: Added ability to run migration from several locations via [[yii\console\controllers\BaseMigrateController::migrationNamespaces]] (klimov-paul)
|
||||||
|
- Enh #9469: Added support for namespaced migrations via [[yii\console\controllers\BaseMigrateController::migrationNamespaces]] (klimov-paul)
|
||||||
|
- Enh #11096: Added support for PSR-2 style migration naming for namespaced migrations (klimov-paul)
|
||||||
- Enh #9708: Added `yii\console\controllers\AssetController::deleteSource` option allowing deletion of the source asset files after compression (pana1990, klimov-paul)
|
- Enh #9708: Added `yii\console\controllers\AssetController::deleteSource` option allowing deletion of the source asset files after compression (pana1990, klimov-paul)
|
||||||
- Enh #10243: Added `yii\data\Sort::setAttributeOrders()` method allowing manual setup of current sort (klimov-paul)
|
- Enh #10243: Added `yii\data\Sort::setAttributeOrders()` method allowing manual setup of current sort (klimov-paul)
|
||||||
- Enh #12440: Added `yii\base\Event::offAll()` method allowing clear all registered class-level event handlers (klimov-paul)
|
- Enh #12440: Added `yii\base\Event::offAll()` method allowing clear all registered class-level event handlers (klimov-paul)
|
||||||
|
@ -8,6 +8,7 @@
|
|||||||
namespace yii\console\controllers;
|
namespace yii\console\controllers;
|
||||||
|
|
||||||
use Yii;
|
use Yii;
|
||||||
|
use yii\base\InvalidConfigException;
|
||||||
use yii\console\Exception;
|
use yii\console\Exception;
|
||||||
use yii\console\Controller;
|
use yii\console\Controller;
|
||||||
use yii\helpers\Console;
|
use yii\helpers\Console;
|
||||||
@ -33,8 +34,30 @@ abstract class BaseMigrateController extends Controller
|
|||||||
/**
|
/**
|
||||||
* @var string the directory storing the migration classes. This can be either
|
* @var string the directory storing the migration classes. This can be either
|
||||||
* a path alias or a directory.
|
* a path alias or a directory.
|
||||||
|
*
|
||||||
|
* You may set this field to `null` in case you have set up [[migrationNamespaces]] in order
|
||||||
|
* to disable usage of migrations without namespace.
|
||||||
*/
|
*/
|
||||||
public $migrationPath = '@app/migrations';
|
public $migrationPath = '@app/migrations';
|
||||||
|
/**
|
||||||
|
* @var array list of namespaces, which are holding migration classes.
|
||||||
|
*
|
||||||
|
* Migration namespace should be available to be resolved as path alias if prefixed with `@`, e.g. if you specify
|
||||||
|
* namespace `app\migrations` code `Yii::getAlias('@app/migrations')` should be able to return file path
|
||||||
|
* to the directory this namespace refers to.
|
||||||
|
*
|
||||||
|
* For example:
|
||||||
|
*
|
||||||
|
* ```php
|
||||||
|
* [
|
||||||
|
* 'app\migrations',
|
||||||
|
* 'some\extension\migrations',
|
||||||
|
* ]
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* @since 2.0.10
|
||||||
|
*/
|
||||||
|
public $migrationNamespaces = [];
|
||||||
/**
|
/**
|
||||||
* @var string the template file for generating new migrations.
|
* @var string the template file for generating new migrations.
|
||||||
* This can be either a path alias (e.g. "@app/migrations/template.php")
|
* This can be either a path alias (e.g. "@app/migrations/template.php")
|
||||||
@ -59,20 +82,30 @@ abstract class BaseMigrateController extends Controller
|
|||||||
* This method is invoked right before an action is to be executed (after all possible filters.)
|
* This method is invoked right before an action is to be executed (after all possible filters.)
|
||||||
* It checks the existence of the [[migrationPath]].
|
* It checks the existence of the [[migrationPath]].
|
||||||
* @param \yii\base\Action $action the action to be executed.
|
* @param \yii\base\Action $action the action to be executed.
|
||||||
* @throws Exception if directory specified in migrationPath doesn't exist and action isn't "create".
|
* @throws InvalidConfigException if directory specified in migrationPath doesn't exist and action isn't "create".
|
||||||
* @return boolean whether the action should continue to be executed.
|
* @return boolean whether the action should continue to be executed.
|
||||||
*/
|
*/
|
||||||
public function beforeAction($action)
|
public function beforeAction($action)
|
||||||
{
|
{
|
||||||
if (parent::beforeAction($action)) {
|
if (parent::beforeAction($action)) {
|
||||||
$path = Yii::getAlias($this->migrationPath);
|
if (empty($this->migrationNamespaces) && empty($this->migrationPath)) {
|
||||||
if (!is_dir($path)) {
|
throw new InvalidConfigException('At least one of `migrationPath` or `migrationNamespaces` should be specified.');
|
||||||
if ($action->id !== 'create') {
|
}
|
||||||
throw new Exception("Migration failed. Directory specified in migrationPath doesn't exist: {$this->migrationPath}");
|
|
||||||
}
|
foreach ($this->migrationNamespaces as $key => $value) {
|
||||||
FileHelper::createDirectory($path);
|
$this->migrationNamespaces[$key] = trim($value, '\\');
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($this->migrationPath !== null) {
|
||||||
|
$path = Yii::getAlias($this->migrationPath);
|
||||||
|
if (!is_dir($path)) {
|
||||||
|
if ($action->id !== 'create') {
|
||||||
|
throw new InvalidConfigException("Migration failed. Directory specified in migrationPath doesn't exist: {$this->migrationPath}");
|
||||||
|
}
|
||||||
|
FileHelper::createDirectory($path);
|
||||||
|
}
|
||||||
|
$this->migrationPath = $path;
|
||||||
}
|
}
|
||||||
$this->migrationPath = $path;
|
|
||||||
|
|
||||||
$version = Yii::getVersion();
|
$version = Yii::getVersion();
|
||||||
$this->stdout("Yii Migration Tool (based on Yii v{$version})\n\n");
|
$this->stdout("Yii Migration Tool (based on Yii v{$version})\n\n");
|
||||||
@ -278,10 +311,11 @@ abstract class BaseMigrateController extends Controller
|
|||||||
* them again. For example,
|
* them again. For example,
|
||||||
*
|
*
|
||||||
* ```
|
* ```
|
||||||
* yii migrate/to 101129_185401 # using timestamp
|
* yii migrate/to 101129_185401 # using timestamp
|
||||||
* yii migrate/to m101129_185401_create_user_table # using full name
|
* yii migrate/to m101129_185401_create_user_table # using full name
|
||||||
* yii migrate/to 1392853618 # using UNIX timestamp
|
* yii migrate/to 1392853618 # using UNIX timestamp
|
||||||
* yii migrate/to "2014-02-15 13:00:50" # using strtotime() parseable string
|
* yii migrate/to "2014-02-15 13:00:50" # using strtotime() parseable string
|
||||||
|
* yii migrate/to app\migrations\M101129185401CreateUser # using full namespace name
|
||||||
* ```
|
* ```
|
||||||
*
|
*
|
||||||
* @param string $version either the version name or the certain time value in the past
|
* @param string $version either the version name or the certain time value in the past
|
||||||
@ -292,14 +326,16 @@ abstract class BaseMigrateController extends Controller
|
|||||||
*/
|
*/
|
||||||
public function actionTo($version)
|
public function actionTo($version)
|
||||||
{
|
{
|
||||||
if (preg_match('/^m?(\d{6}_\d{6})(_.*?)?$/', $version, $matches)) {
|
if (($namespaceVersion = $this->extractNamespaceMigrationVersion($version)) !== false) {
|
||||||
$this->migrateToVersion('m' . $matches[1]);
|
$this->migrateToVersion($namespaceVersion);
|
||||||
|
} elseif (($migrationName = $this->extractMigrationVersion($version)) !== false) {
|
||||||
|
$this->migrateToVersion($migrationName);
|
||||||
} elseif ((string) (int) $version == $version) {
|
} elseif ((string) (int) $version == $version) {
|
||||||
$this->migrateToTime($version);
|
$this->migrateToTime($version);
|
||||||
} elseif (($time = strtotime($version)) !== false) {
|
} elseif (($time = strtotime($version)) !== false) {
|
||||||
$this->migrateToTime($time);
|
$this->migrateToTime($time);
|
||||||
} else {
|
} else {
|
||||||
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("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 the full namespaced name of a migration (e.g. app\\migrations\\M101129185401CreateUserTable),\n a UNIX timestamp (e.g. 1392853000), or a datetime string parseable\nby the strtotime() function (e.g. 2014-02-15 13:00:50).");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -309,8 +345,9 @@ abstract class BaseMigrateController extends Controller
|
|||||||
* No actual migration will be performed.
|
* No actual migration will be performed.
|
||||||
*
|
*
|
||||||
* ```
|
* ```
|
||||||
* yii migrate/mark 101129_185401 # using timestamp
|
* yii migrate/mark 101129_185401 # using timestamp
|
||||||
* yii migrate/mark m101129_185401_create_user_table # using full name
|
* yii migrate/mark m101129_185401_create_user_table # using full name
|
||||||
|
* yii migrate/to app\migrations\M101129185401CreateUser # using full namespace name
|
||||||
* ```
|
* ```
|
||||||
*
|
*
|
||||||
* @param string $version the version at which the migration history should be marked.
|
* @param string $version the version at which the migration history should be marked.
|
||||||
@ -321,16 +358,18 @@ abstract class BaseMigrateController extends Controller
|
|||||||
public function actionMark($version)
|
public function actionMark($version)
|
||||||
{
|
{
|
||||||
$originalVersion = $version;
|
$originalVersion = $version;
|
||||||
if (preg_match('/^m?(\d{6}_\d{6})(_.*?)?$/', $version, $matches)) {
|
if (($namespaceVersion = $this->extractNamespaceMigrationVersion($version)) !== false) {
|
||||||
$version = 'm' . $matches[1];
|
$version = $namespaceVersion;
|
||||||
|
} elseif (($migrationName = $this->extractMigrationVersion($version)) !== false) {
|
||||||
|
$version = $migrationName;
|
||||||
} else {
|
} 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).");
|
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)\nor the full name of a namespaced migration (e.g. app\\migrations\\M101129185401CreateUserTable).");
|
||||||
}
|
}
|
||||||
|
|
||||||
// try mark up
|
// try mark up
|
||||||
$migrations = $this->getNewMigrations();
|
$migrations = $this->getNewMigrations();
|
||||||
foreach ($migrations as $i => $migration) {
|
foreach ($migrations as $i => $migration) {
|
||||||
if (strpos($migration, $version . '_') === 0) {
|
if (strpos($migration, $version) === 0) {
|
||||||
if ($this->confirm("Set migration history at $originalVersion?")) {
|
if ($this->confirm("Set migration history at $originalVersion?")) {
|
||||||
for ($j = 0; $j <= $i; ++$j) {
|
for ($j = 0; $j <= $i; ++$j) {
|
||||||
$this->addMigrationHistory($migrations[$j]);
|
$this->addMigrationHistory($migrations[$j]);
|
||||||
@ -345,7 +384,7 @@ abstract class BaseMigrateController extends Controller
|
|||||||
// try mark down
|
// try mark down
|
||||||
$migrations = array_keys($this->getMigrationHistory(null));
|
$migrations = array_keys($this->getMigrationHistory(null));
|
||||||
foreach ($migrations as $i => $migration) {
|
foreach ($migrations as $i => $migration) {
|
||||||
if (strpos($migration, $version . '_') === 0) {
|
if (strpos($migration, $version) === 0) {
|
||||||
if ($i === 0) {
|
if ($i === 0) {
|
||||||
$this->stdout("Already at '$originalVersion'. Nothing needs to be done.\n", Console::FG_YELLOW);
|
$this->stdout("Already at '$originalVersion'. Nothing needs to be done.\n", Console::FG_YELLOW);
|
||||||
} else {
|
} else {
|
||||||
@ -364,6 +403,34 @@ abstract class BaseMigrateController extends Controller
|
|||||||
throw new Exception("Unable to find the version '$originalVersion'.");
|
throw new Exception("Unable to find the version '$originalVersion'.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if given migration version specification matches namespaced migration name.
|
||||||
|
* @param string $rawVersion raw version specification received from user input.
|
||||||
|
* @return string|false actual migration version, `false` - if not match.
|
||||||
|
* @since 2.0.10
|
||||||
|
*/
|
||||||
|
private function extractNamespaceMigrationVersion($rawVersion)
|
||||||
|
{
|
||||||
|
if (preg_match('/^\\\\?([\w_]+\\\\)+m(\d{6}_?\d{6})(\D.*?)?$/is', $rawVersion, $matches)) {
|
||||||
|
return trim($rawVersion, '\\');
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if given migration version specification matches migration base name.
|
||||||
|
* @param string $rawVersion raw version specification received from user input.
|
||||||
|
* @return string|false actual migration version, `false` - if not match.
|
||||||
|
* @since 2.0.10
|
||||||
|
*/
|
||||||
|
private function extractMigrationVersion($rawVersion)
|
||||||
|
{
|
||||||
|
if (preg_match('/^m?(\d{6}_?\d{6})(\D.*?)?$/is', $rawVersion, $matches)) {
|
||||||
|
return 'm' . $matches[1];
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Displays the migration history.
|
* Displays the migration history.
|
||||||
*
|
*
|
||||||
@ -465,8 +532,19 @@ abstract class BaseMigrateController extends Controller
|
|||||||
* yii migrate/create create_user_table
|
* yii migrate/create create_user_table
|
||||||
* ```
|
* ```
|
||||||
*
|
*
|
||||||
|
* In order to generate namespaced migration you should specify namespace before migration's name.
|
||||||
|
* Note that backslash (`\`) usually is considered as a special char in console, so you need to escape argument
|
||||||
|
* properly to avoid shell error or incorrect behavior.
|
||||||
|
* For example:
|
||||||
|
*
|
||||||
|
* ```
|
||||||
|
* yii migrate/create 'app\\migrations\\createUserTable'
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* In case [[migrationPath]] is not set and no namespace provided the first entry of [[migrationNamespaces]] will be used.
|
||||||
|
*
|
||||||
* @param string $name the name of the new migration. This should only contain
|
* @param string $name the name of the new migration. This should only contain
|
||||||
* letters, digits and/or underscores.
|
* letters, digits, underscores and/or backslashes.
|
||||||
*
|
*
|
||||||
* Note: If the migration name is of a special form, for example create_xxx or
|
* Note: If the migration name is of a special form, for example create_xxx or
|
||||||
* drop_xxx then the generated migration file will contain extra code,
|
* drop_xxx then the generated migration file will contain extra code,
|
||||||
@ -476,22 +554,75 @@ abstract class BaseMigrateController extends Controller
|
|||||||
*/
|
*/
|
||||||
public function actionCreate($name)
|
public function actionCreate($name)
|
||||||
{
|
{
|
||||||
if (!preg_match('/^\w+$/', $name)) {
|
if (!preg_match('/^[\w\\\\]+$/', $name)) {
|
||||||
throw new Exception('The migration name should contain letters, digits and/or underscore characters only.');
|
throw new Exception('The migration name should contain letters, digits, underscore and/or backslash characters only.');
|
||||||
}
|
}
|
||||||
|
|
||||||
$className = 'm' . gmdate('ymd_His') . '_' . $name;
|
list($namespace, $className) = $this->generateClassName($name);
|
||||||
$file = $this->migrationPath . DIRECTORY_SEPARATOR . $className . '.php';
|
$migrationPath = $this->findMigrationPath($namespace);
|
||||||
|
|
||||||
|
$file = $migrationPath . DIRECTORY_SEPARATOR . $className . '.php';
|
||||||
if ($this->confirm("Create new migration '$file'?")) {
|
if ($this->confirm("Create new migration '$file'?")) {
|
||||||
$content = $this->generateMigrationSourceCode([
|
$content = $this->generateMigrationSourceCode([
|
||||||
'name' => $name,
|
'name' => $name,
|
||||||
'className' => $className,
|
'className' => $className,
|
||||||
|
'namespace' => $namespace,
|
||||||
]);
|
]);
|
||||||
|
FileHelper::createDirectory($migrationPath);
|
||||||
file_put_contents($file, $content);
|
file_put_contents($file, $content);
|
||||||
$this->stdout("New migration created successfully.\n", Console::FG_GREEN);
|
$this->stdout("New migration created successfully.\n", Console::FG_GREEN);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates class base name and namespace from migration name from user input.
|
||||||
|
* @param string $name migration name from user input.
|
||||||
|
* @return array list of 2 elements: 'namespace' and 'class base name'
|
||||||
|
* @since 2.0.10
|
||||||
|
*/
|
||||||
|
private function generateClassName($name)
|
||||||
|
{
|
||||||
|
$namespace = null;
|
||||||
|
$name = trim($name, '\\');
|
||||||
|
if (strpos($name, '\\') !== false) {
|
||||||
|
$namespace = substr($name, 0, strrpos($name, '\\'));
|
||||||
|
$name = substr($name, strrpos($name, '\\') + 1);
|
||||||
|
} else {
|
||||||
|
if ($this->migrationPath === null) {
|
||||||
|
$migrationNamespaces = $this->migrationNamespaces;
|
||||||
|
$namespace = array_shift($migrationNamespaces);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($namespace === null) {
|
||||||
|
$class = 'm' . gmdate('ymd_His') . '_' . $name;
|
||||||
|
} else {
|
||||||
|
$class = 'M' . gmdate('ymdHis') . ucfirst($name);
|
||||||
|
}
|
||||||
|
|
||||||
|
return [$namespace, $class];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Finds the file path for the specified migration namespace.
|
||||||
|
* @param string|null $namespace migration namespace.
|
||||||
|
* @return string migration file path.
|
||||||
|
* @throws Exception on failure.
|
||||||
|
* @since 2.0.10
|
||||||
|
*/
|
||||||
|
private function findMigrationPath($namespace)
|
||||||
|
{
|
||||||
|
if (empty($namespace)) {
|
||||||
|
return $this->migrationPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!in_array($namespace, $this->migrationNamespaces, true)) {
|
||||||
|
throw new Exception("Namespace '{$namespace}' is not mentioned among `migrationNamespaces`");
|
||||||
|
}
|
||||||
|
|
||||||
|
return Yii::getAlias('@' . str_replace('\\', DIRECTORY_SEPARATOR, $namespace));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Upgrades with the specified migration class.
|
* Upgrades with the specified migration class.
|
||||||
* @param string $class the migration class name
|
* @param string $class the migration class name
|
||||||
@ -539,7 +670,6 @@ abstract class BaseMigrateController extends Controller
|
|||||||
$time = microtime(true) - $start;
|
$time = microtime(true) - $start;
|
||||||
$this->stdout("*** reverted $class (time: " . sprintf('%.3f', $time) . "s)\n\n", Console::FG_GREEN);
|
$this->stdout("*** reverted $class (time: " . sprintf('%.3f', $time) . "s)\n\n", Console::FG_GREEN);
|
||||||
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
$time = microtime(true) - $start;
|
$time = microtime(true) - $start;
|
||||||
@ -556,8 +686,11 @@ abstract class BaseMigrateController extends Controller
|
|||||||
*/
|
*/
|
||||||
protected function createMigration($class)
|
protected function createMigration($class)
|
||||||
{
|
{
|
||||||
$file = $this->migrationPath . DIRECTORY_SEPARATOR . $class . '.php';
|
$class = trim($class, '\\');
|
||||||
require_once($file);
|
if (strpos($class, '\\') === false) {
|
||||||
|
$file = $this->migrationPath . DIRECTORY_SEPARATOR . $class . '.php';
|
||||||
|
require_once($file);
|
||||||
|
}
|
||||||
|
|
||||||
return new $class();
|
return new $class();
|
||||||
}
|
}
|
||||||
@ -593,7 +726,7 @@ abstract class BaseMigrateController extends Controller
|
|||||||
// try migrate up
|
// try migrate up
|
||||||
$migrations = $this->getNewMigrations();
|
$migrations = $this->getNewMigrations();
|
||||||
foreach ($migrations as $i => $migration) {
|
foreach ($migrations as $i => $migration) {
|
||||||
if (strpos($migration, $version . '_') === 0) {
|
if (strpos($migration, $version) === 0) {
|
||||||
$this->actionUp($i + 1);
|
$this->actionUp($i + 1);
|
||||||
|
|
||||||
return self::EXIT_CODE_NORMAL;
|
return self::EXIT_CODE_NORMAL;
|
||||||
@ -603,7 +736,7 @@ abstract class BaseMigrateController extends Controller
|
|||||||
// try migrate down
|
// try migrate down
|
||||||
$migrations = array_keys($this->getMigrationHistory(null));
|
$migrations = array_keys($this->getMigrationHistory(null));
|
||||||
foreach ($migrations as $i => $migration) {
|
foreach ($migrations as $i => $migration) {
|
||||||
if (strpos($migration, $version . '_') === 0) {
|
if (strpos($migration, $version) === 0) {
|
||||||
if ($i === 0) {
|
if ($i === 0) {
|
||||||
$this->stdout("Already at '$originalVersion'. Nothing needs to be done.\n", Console::FG_YELLOW);
|
$this->stdout("Already at '$originalVersion'. Nothing needs to be done.\n", Console::FG_YELLOW);
|
||||||
} else {
|
} else {
|
||||||
@ -624,25 +757,45 @@ abstract class BaseMigrateController extends Controller
|
|||||||
protected function getNewMigrations()
|
protected function getNewMigrations()
|
||||||
{
|
{
|
||||||
$applied = [];
|
$applied = [];
|
||||||
foreach ($this->getMigrationHistory(null) as $version => $time) {
|
foreach ($this->getMigrationHistory(null) as $class => $time) {
|
||||||
$applied[substr($version, 1, 13)] = true;
|
$applied[trim($class, '\\')] = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
$migrationPaths = [];
|
||||||
|
if (!empty($this->migrationPath)) {
|
||||||
|
$migrationPaths[''] = $this->migrationPath;
|
||||||
|
}
|
||||||
|
foreach ($this->migrationNamespaces as $namespace) {
|
||||||
|
$migrationPaths[$namespace] = Yii::getAlias('@' . str_replace('\\', DIRECTORY_SEPARATOR, $namespace));
|
||||||
}
|
}
|
||||||
|
|
||||||
$migrations = [];
|
$migrations = [];
|
||||||
$handle = opendir($this->migrationPath);
|
foreach ($migrationPaths as $namespace => $migrationPath) {
|
||||||
while (($file = readdir($handle)) !== false) {
|
if (!file_exists($migrationPath)) {
|
||||||
if ($file === '.' || $file === '..') {
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
$path = $this->migrationPath . DIRECTORY_SEPARATOR . $file;
|
$handle = opendir($migrationPath);
|
||||||
if (preg_match('/^(m(\d{6}_\d{6})_.*?)\.php$/', $file, $matches) && !isset($applied[$matches[2]]) && is_file($path)) {
|
while (($file = readdir($handle)) !== false) {
|
||||||
$migrations[] = $matches[1];
|
if ($file === '.' || $file === '..') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$path = $migrationPath . DIRECTORY_SEPARATOR . $file;
|
||||||
|
if (preg_match('/^(m(\d{6}_?\d{6})\D.*?)\.php$/is', $file, $matches) && is_file($path)) {
|
||||||
|
$class = $matches[1];
|
||||||
|
if (!empty($namespace)) {
|
||||||
|
$class = $namespace . '\\' . $class;
|
||||||
|
}
|
||||||
|
$time = str_replace('_', '', $matches[2]);
|
||||||
|
if (!isset($applied[$class])) {
|
||||||
|
$migrations[$time . '\\' . $class] = $class;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
closedir($handle);
|
||||||
}
|
}
|
||||||
closedir($handle);
|
ksort($migrations);
|
||||||
sort($migrations);
|
|
||||||
|
|
||||||
return $migrations;
|
return array_values($migrations);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -50,6 +50,24 @@ use yii\helpers\Console;
|
|||||||
* yii migrate/down
|
* yii migrate/down
|
||||||
* ```
|
* ```
|
||||||
*
|
*
|
||||||
|
* Since 2.0.10 you can use namespaced migrations. In order to enable this feature you should configure [[migrationNamespaces]]
|
||||||
|
* property for the controller at application configuration:
|
||||||
|
*
|
||||||
|
* ```php
|
||||||
|
* return [
|
||||||
|
* 'controllerMap' => [
|
||||||
|
* 'migrate' => [
|
||||||
|
* 'class' => 'yii\console\controllers\MigrateController',
|
||||||
|
* 'migrationNamespaces' => [
|
||||||
|
* 'app\migrations',
|
||||||
|
* 'some\extension\migrations',
|
||||||
|
* ],
|
||||||
|
* //'migrationPath' => null, // allows to disable not namespaced migration completely
|
||||||
|
* ],
|
||||||
|
* ],
|
||||||
|
* ];
|
||||||
|
* ```
|
||||||
|
*
|
||||||
* @author Qiang Xue <qiang.xue@gmail.com>
|
* @author Qiang Xue <qiang.xue@gmail.com>
|
||||||
* @since 2.0
|
* @since 2.0
|
||||||
*/
|
*/
|
||||||
@ -164,8 +182,11 @@ class MigrateController extends BaseMigrateController
|
|||||||
*/
|
*/
|
||||||
protected function createMigration($class)
|
protected function createMigration($class)
|
||||||
{
|
{
|
||||||
$file = $this->migrationPath . DIRECTORY_SEPARATOR . $class . '.php';
|
$class = trim($class, '\\');
|
||||||
require_once($file);
|
if (strpos($class, '\\') === false) {
|
||||||
|
$file = $this->migrationPath . DIRECTORY_SEPARATOR . $class . '.php';
|
||||||
|
require_once($file);
|
||||||
|
}
|
||||||
|
|
||||||
return new $class(['db' => $this->db]);
|
return new $class(['db' => $this->db]);
|
||||||
}
|
}
|
||||||
|
@ -3,7 +3,8 @@
|
|||||||
* This view is used by console/controllers/MigrateController.php
|
* This view is used by console/controllers/MigrateController.php
|
||||||
* The following variables are available in this view:
|
* The following variables are available in this view:
|
||||||
*/
|
*/
|
||||||
/* @var $className string the new migration class name */
|
/* @var $className string the new migration class name without namespace */
|
||||||
|
/* @var $namespace string the new migration class namespace */
|
||||||
/* @var $table string the name table */
|
/* @var $table string the name table */
|
||||||
/* @var $fields array the fields */
|
/* @var $fields array the fields */
|
||||||
|
|
||||||
@ -11,6 +12,9 @@ preg_match('/^add_(.+)_columns?_to_(.+)_table$/', $name, $matches);
|
|||||||
$columns = $matches[1];
|
$columns = $matches[1];
|
||||||
|
|
||||||
echo "<?php\n";
|
echo "<?php\n";
|
||||||
|
if (!empty($namespace)) {
|
||||||
|
echo "\nnamespace {$namespace};\n";
|
||||||
|
}
|
||||||
?>
|
?>
|
||||||
|
|
||||||
use yii\db\Migration;
|
use yii\db\Migration;
|
||||||
|
@ -5,12 +5,16 @@
|
|||||||
* @since 2.0.7
|
* @since 2.0.7
|
||||||
* @deprecated since 2.0.8
|
* @deprecated since 2.0.8
|
||||||
*/
|
*/
|
||||||
/* @var $className string the new migration class name */
|
/* @var $className string the new migration class name without namespace */
|
||||||
|
/* @var $namespace string the new migration class namespace */
|
||||||
/* @var $table string the name table */
|
/* @var $table string the name table */
|
||||||
/* @var $field_first string the name field first */
|
/* @var $field_first string the name field first */
|
||||||
/* @var $field_second string the name field second */
|
/* @var $field_second string the name field second */
|
||||||
|
|
||||||
echo "<?php\n";
|
echo "<?php\n";
|
||||||
|
if (!empty($namespace)) {
|
||||||
|
echo "\nnamespace {$namespace};\n";
|
||||||
|
}
|
||||||
?>
|
?>
|
||||||
|
|
||||||
use yii\db\Migration;
|
use yii\db\Migration;
|
||||||
|
@ -3,12 +3,16 @@
|
|||||||
* This view is used by console/controllers/MigrateController.php
|
* This view is used by console/controllers/MigrateController.php
|
||||||
* The following variables are available in this view:
|
* The following variables are available in this view:
|
||||||
*/
|
*/
|
||||||
/* @var $className string the new migration class name */
|
/* @var $className string the new migration class name without namespace */
|
||||||
|
/* @var $namespace string the new migration class namespace */
|
||||||
/* @var $table string the name table */
|
/* @var $table string the name table */
|
||||||
/* @var $fields array the fields */
|
/* @var $fields array the fields */
|
||||||
/* @var $foreignKeys array the foreign keys */
|
/* @var $foreignKeys array the foreign keys */
|
||||||
|
|
||||||
echo "<?php\n";
|
echo "<?php\n";
|
||||||
|
if (!empty($namespace)) {
|
||||||
|
echo "\nnamespace {$namespace};\n";
|
||||||
|
}
|
||||||
?>
|
?>
|
||||||
|
|
||||||
use yii\db\Migration;
|
use yii\db\Migration;
|
||||||
|
@ -3,13 +3,17 @@
|
|||||||
* This view is used by console/controllers/MigrateController.php
|
* This view is used by console/controllers/MigrateController.php
|
||||||
* The following variables are available in this view:
|
* The following variables are available in this view:
|
||||||
*/
|
*/
|
||||||
/* @var $className string the new migration class name */
|
/* @var $className string the new migration class name without namespace */
|
||||||
|
/* @var $namespace string the new migration class namespace */
|
||||||
/* @var $table string the name table */
|
/* @var $table string the name table */
|
||||||
/* @var $fields array the fields */
|
/* @var $fields array the fields */
|
||||||
preg_match('/^drop_(.+)_columns?_from_(.+)_table$/', $name, $matches);
|
preg_match('/^drop_(.+)_columns?_from_(.+)_table$/', $name, $matches);
|
||||||
$columns = $matches[1];
|
$columns = $matches[1];
|
||||||
|
|
||||||
echo "<?php\n";
|
echo "<?php\n";
|
||||||
|
if (!empty($namespace)) {
|
||||||
|
echo "\nnamespace {$namespace};\n";
|
||||||
|
}
|
||||||
?>
|
?>
|
||||||
|
|
||||||
use yii\db\Migration;
|
use yii\db\Migration;
|
||||||
|
@ -3,11 +3,15 @@
|
|||||||
* This view is used by console/controllers/MigrateController.php
|
* This view is used by console/controllers/MigrateController.php
|
||||||
* The following variables are available in this view:
|
* The following variables are available in this view:
|
||||||
*/
|
*/
|
||||||
/* @var $className string the new migration class name */
|
/* @var $className string the new migration class name without namespace */
|
||||||
|
/* @var $namespace string the new migration class namespace */
|
||||||
/* @var $table string the name table */
|
/* @var $table string the name table */
|
||||||
/* @var $fields array the fields */
|
/* @var $fields array the fields */
|
||||||
|
|
||||||
echo "<?php\n";
|
echo "<?php\n";
|
||||||
|
if (!empty($namespace)) {
|
||||||
|
echo "\nnamespace {$namespace};\n";
|
||||||
|
}
|
||||||
?>
|
?>
|
||||||
|
|
||||||
use yii\db\Migration;
|
use yii\db\Migration;
|
||||||
|
@ -3,9 +3,13 @@
|
|||||||
* This view is used by console/controllers/MigrateController.php
|
* This view is used by console/controllers/MigrateController.php
|
||||||
* The following variables are available in this view:
|
* The following variables are available in this view:
|
||||||
*/
|
*/
|
||||||
/* @var $className string the new migration class name */
|
/* @var $className string the new migration class name without namespace */
|
||||||
|
/* @var $namespace string the new migration class namespace */
|
||||||
|
|
||||||
echo "<?php\n";
|
echo "<?php\n";
|
||||||
|
if (!empty($namespace)) {
|
||||||
|
echo "\nnamespace {$namespace};\n";
|
||||||
|
}
|
||||||
?>
|
?>
|
||||||
|
|
||||||
use yii\db\Migration;
|
use yii\db\Migration;
|
||||||
|
@ -50,4 +50,156 @@ class MigrateControllerTest extends TestCase
|
|||||||
$query = new Query();
|
$query = new Query();
|
||||||
return $query->from('migration')->all();
|
return $query->from('migration')->all();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected function assertFileContent($expectedFile, $class)
|
||||||
|
{
|
||||||
|
$this->assertEqualsWithoutLE(
|
||||||
|
include Yii::getAlias("@yiiunit/data/console/migrate_create/$expectedFile.php"),
|
||||||
|
$this->parseNameClassMigration($class)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function assertCommandCreatedFile($expectedFile, $migrationName, $params = [])
|
||||||
|
{
|
||||||
|
$class = 'm' . gmdate('ymd_His') . '_' . $migrationName;
|
||||||
|
$params[0] = $migrationName;
|
||||||
|
$this->runMigrateControllerAction('create', $params);
|
||||||
|
$this->assertFileContent($expectedFile, $class);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tests :
|
||||||
|
|
||||||
|
public function testGenerateDefaultMigration()
|
||||||
|
{
|
||||||
|
$this->assertCommandCreatedFile('default', 'DefaultTest');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testGenerateCreateMigration()
|
||||||
|
{
|
||||||
|
$migrationNames = [
|
||||||
|
'create_test_table',
|
||||||
|
];
|
||||||
|
foreach ($migrationNames as $migrationName) {
|
||||||
|
$this->assertCommandCreatedFile('create_test', $migrationName);
|
||||||
|
|
||||||
|
$this->assertCommandCreatedFile('create_fields', $migrationName, [
|
||||||
|
'fields' => 'title:string(10):notNull:unique:defaultValue("test"),
|
||||||
|
body:text:notNull,
|
||||||
|
price:money(11,2):notNull,
|
||||||
|
parenthesis_in_comment:string(255):notNull:comment(\'Name of set (RU)\')'
|
||||||
|
]);
|
||||||
|
|
||||||
|
$this->assertCommandCreatedFile('create_title_pk', $migrationName, [
|
||||||
|
'fields' => 'title:primaryKey,body:text:notNull,price:money(11,2)',
|
||||||
|
]);
|
||||||
|
|
||||||
|
$this->assertCommandCreatedFile('create_id_pk', $migrationName, [
|
||||||
|
'fields' => 'id:primaryKey,
|
||||||
|
address:string,
|
||||||
|
address2:string,
|
||||||
|
email:string',
|
||||||
|
]);
|
||||||
|
|
||||||
|
$this->assertCommandCreatedFile('create_foreign_key', $migrationName, [
|
||||||
|
'fields' => 'user_id:integer:foreignKey,
|
||||||
|
product_id:foreignKey:integer:unsigned:notNull,
|
||||||
|
order_id:integer:foreignKey(user_order):notNull,
|
||||||
|
created_at:dateTime:notNull',
|
||||||
|
]);
|
||||||
|
|
||||||
|
$this->assertCommandCreatedFile('create_prefix', $migrationName, [
|
||||||
|
'useTablePrefix' => true,
|
||||||
|
'fields' => 'user_id:integer:foreignKey,
|
||||||
|
product_id:foreignKey:integer:unsigned:notNull,
|
||||||
|
order_id:integer:foreignKey(user_order):notNull,
|
||||||
|
created_at:dateTime:notNull',
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// @see https://github.com/yiisoft/yii2/issues/10876
|
||||||
|
$this->assertCommandCreatedFile('create_products_from_store_table', 'create_products_from_store_table');
|
||||||
|
|
||||||
|
// @see https://github.com/yiisoft/yii2/issues/11461
|
||||||
|
$this->assertCommandCreatedFile('create_title_with_comma_default_values', 'create_test_table', [
|
||||||
|
'fields' => 'title:string(10):notNull:unique:defaultValue(",te,st"),
|
||||||
|
body:text:notNull:defaultValue(",test"),
|
||||||
|
test:custom(11,2,"s"):notNull',
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testGenerateDropMigration()
|
||||||
|
{
|
||||||
|
$migrationNames = [
|
||||||
|
'drop_test_table',
|
||||||
|
];
|
||||||
|
foreach ($migrationNames as $migrationName) {
|
||||||
|
$this->assertCommandCreatedFile('drop_test', $migrationName);
|
||||||
|
|
||||||
|
$this->assertCommandCreatedFile('drop_fields', $migrationName, [
|
||||||
|
'fields' => 'body:text:notNull,price:money(11,2)'
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// @see https://github.com/yiisoft/yii2/issues/10876
|
||||||
|
$this->assertCommandCreatedFile('drop_products_from_store_table', 'drop_products_from_store_table');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testGenerateAddColumnMigration()
|
||||||
|
{
|
||||||
|
$migrationNames = [
|
||||||
|
'add_columns_column_to_test_table',
|
||||||
|
'add_columns_columns_to_test_table',
|
||||||
|
];
|
||||||
|
foreach ($migrationNames as $migrationName) {
|
||||||
|
$this->assertCommandCreatedFile('add_columns_test', $migrationName, [
|
||||||
|
'fields' => 'title:string(10):notNull,
|
||||||
|
body:text:notNull,
|
||||||
|
price:money(11,2):notNull,
|
||||||
|
created_at:dateTime'
|
||||||
|
]);
|
||||||
|
|
||||||
|
$this->assertCommandCreatedFile('add_columns_fk', $migrationName, [
|
||||||
|
'fields' => 'user_id:integer:foreignKey,
|
||||||
|
product_id:foreignKey:integer:unsigned:notNull,
|
||||||
|
order_id:integer:foreignKey(user_order):notNull,
|
||||||
|
created_at:dateTime:notNull',
|
||||||
|
]);
|
||||||
|
|
||||||
|
$this->assertCommandCreatedFile('add_columns_prefix', $migrationName, [
|
||||||
|
'useTablePrefix' => true,
|
||||||
|
'fields' => 'user_id:integer:foreignKey,
|
||||||
|
product_id:foreignKey:integer:unsigned:notNull,
|
||||||
|
order_id:integer:foreignKey(user_order):notNull,
|
||||||
|
created_at:dateTime:notNull',
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testGenerateDropColumnMigration()
|
||||||
|
{
|
||||||
|
$migrationNames = [
|
||||||
|
'drop_columns_column_from_test_table',
|
||||||
|
'drop_columns_columns_from_test_table',
|
||||||
|
];
|
||||||
|
foreach ($migrationNames as $migrationName) {
|
||||||
|
$this->assertCommandCreatedFile('drop_columns_test', $migrationName, [
|
||||||
|
'fields' => 'title:string(10):notNull,body:text:notNull,
|
||||||
|
price:money(11,2):notNull,
|
||||||
|
created_at:dateTime'
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testGenerateCreateJunctionMigration()
|
||||||
|
{
|
||||||
|
$migrationNames = [
|
||||||
|
'create_junction_post_and_tag_tables',
|
||||||
|
'create_junction_for_post_and_tag_tables',
|
||||||
|
'create_junction_table_for_post_and_tag_tables',
|
||||||
|
'create_junction_table_for_post_and_tag_table',
|
||||||
|
];
|
||||||
|
foreach ($migrationNames as $migrationName) {
|
||||||
|
$this->assertCommandCreatedFile('junction_test', $migrationName);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
@ -27,10 +27,15 @@ trait MigrateControllerTestTrait
|
|||||||
* @var string test migration path.
|
* @var string test migration path.
|
||||||
*/
|
*/
|
||||||
protected $migrationPath;
|
protected $migrationPath;
|
||||||
|
/**
|
||||||
|
* @var string test migration namespace
|
||||||
|
*/
|
||||||
|
protected $migrationNamespace;
|
||||||
|
|
||||||
|
|
||||||
public function setUpMigrationPath()
|
public function setUpMigrationPath()
|
||||||
{
|
{
|
||||||
|
$this->migrationNamespace = 'yiiunit\runtime\test_migrations';
|
||||||
$this->migrationPath = Yii::getAlias('@yiiunit/runtime/test_migrations');
|
$this->migrationPath = Yii::getAlias('@yiiunit/runtime/test_migrations');
|
||||||
FileHelper::createDirectory($this->migrationPath);
|
FileHelper::createDirectory($this->migrationPath);
|
||||||
if (!file_exists($this->migrationPath)) {
|
if (!file_exists($this->migrationPath)) {
|
||||||
@ -43,26 +48,7 @@ trait MigrateControllerTestTrait
|
|||||||
FileHelper::removeDirectory($this->migrationPath);
|
FileHelper::removeDirectory($this->migrationPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function assertFileContent($expectedFile, $class)
|
|
||||||
{
|
|
||||||
$this->assertEqualsWithoutLE(
|
|
||||||
include Yii::getAlias(
|
|
||||||
"@yiiunit/data/console/migrate_create/$expectedFile.php"
|
|
||||||
),
|
|
||||||
$this->parseNameClassMigration($class)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function assertCommandCreatedFile(
|
|
||||||
$expectedFile,
|
|
||||||
$migrationName,
|
|
||||||
$params = []
|
|
||||||
) {
|
|
||||||
$class = 'm' . gmdate('ymd_His') . '_' . $migrationName;
|
|
||||||
$params[0] = $migrationName;
|
|
||||||
$this->runMigrateControllerAction('create', $params);
|
|
||||||
$this->assertFileContent($expectedFile, $class);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return array applied migration entries
|
* @return array applied migration entries
|
||||||
@ -71,27 +57,29 @@ trait MigrateControllerTestTrait
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates test migrate controller instance.
|
* Creates test migrate controller instance.
|
||||||
|
* @param array $config controller configuration.
|
||||||
* @return BaseMigrateController migrate command instance.
|
* @return BaseMigrateController migrate command instance.
|
||||||
*/
|
*/
|
||||||
protected function createMigrateController()
|
protected function createMigrateController(array $config = [])
|
||||||
{
|
{
|
||||||
$module = $this->getMock('yii\\base\\Module', ['fake'], ['console']);
|
$module = $this->getMock('yii\\base\\Module', ['fake'], ['console']);
|
||||||
$class = $this->migrateControllerClass;
|
$class = $this->migrateControllerClass;
|
||||||
$migrateController = new $class('migrate', $module);
|
$migrateController = new $class('migrate', $module);
|
||||||
$migrateController->interactive = false;
|
$migrateController->interactive = false;
|
||||||
$migrateController->migrationPath = $this->migrationPath;
|
$migrateController->migrationPath = $this->migrationPath;
|
||||||
return $migrateController;
|
return Yii::configure($migrateController, $config);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Emulates running of the migrate controller action.
|
* Emulates running of the migrate controller action.
|
||||||
* @param string $actionID id of action to be run.
|
* @param string $actionID id of action to be run.
|
||||||
* @param array $args action arguments.
|
* @param array $args action arguments.
|
||||||
|
* @param array $config controller configuration.
|
||||||
* @return string command output.
|
* @return string command output.
|
||||||
*/
|
*/
|
||||||
protected function runMigrateControllerAction($actionID, array $args = [])
|
protected function runMigrateControllerAction($actionID, array $args = [], array $config = [])
|
||||||
{
|
{
|
||||||
$controller = $this->createMigrateController();
|
$controller = $this->createMigrateController($config);
|
||||||
ob_start();
|
ob_start();
|
||||||
ob_implicit_flush(false);
|
ob_implicit_flush(false);
|
||||||
$controller->run($actionID, $args);
|
$controller->run($actionID, $args);
|
||||||
@ -130,6 +118,40 @@ CODE;
|
|||||||
return $class;
|
return $class;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $name
|
||||||
|
* @param string|null $date
|
||||||
|
* @return string generated class name
|
||||||
|
*/
|
||||||
|
protected function createNamespaceMigration($name, $date = null)
|
||||||
|
{
|
||||||
|
if ($date === null) {
|
||||||
|
$date = gmdate('ymdHis');
|
||||||
|
}
|
||||||
|
$class = 'M' . $date . ucfirst($name);
|
||||||
|
$baseClass = $this->migrationBaseClass;
|
||||||
|
$namespace = $this->migrationNamespace;
|
||||||
|
|
||||||
|
$code = <<<CODE
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace {$namespace};
|
||||||
|
|
||||||
|
class {$class} extends \\{$baseClass}
|
||||||
|
{
|
||||||
|
public function up()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public function down()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
CODE;
|
||||||
|
file_put_contents($this->migrationPath . DIRECTORY_SEPARATOR . $class . '.php', $code);
|
||||||
|
return $class;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Change class name migration to $class
|
* Change class name migration to $class
|
||||||
* @param string $class name class
|
* @param string $class name class
|
||||||
@ -159,7 +181,7 @@ CODE;
|
|||||||
$appliedMigrations = $migrationHistory;
|
$appliedMigrations = $migrationHistory;
|
||||||
foreach ($expectedMigrations as $expectedMigrationName) {
|
foreach ($expectedMigrations as $expectedMigrationName) {
|
||||||
$appliedMigration = array_shift($appliedMigrations);
|
$appliedMigration = array_shift($appliedMigrations);
|
||||||
if (strpos($appliedMigration['version'], $expectedMigrationName) === false) {
|
if (!fnmatch(strtr($expectedMigrationName, ['\\' => DIRECTORY_SEPARATOR]), strtr($appliedMigration['version'], ['\\' => DIRECTORY_SEPARATOR]))) {
|
||||||
$success = false;
|
$success = false;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -188,139 +210,7 @@ CODE;
|
|||||||
$this->assertContains($migrationName, basename($files[0]), 'Wrong migration name!');
|
$this->assertContains($migrationName, basename($files[0]), 'Wrong migration name!');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testGenerateDefaultMigration()
|
|
||||||
{
|
|
||||||
$this->assertCommandCreatedFile('default', 'DefaultTest');
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testGenerateCreateMigration()
|
|
||||||
{
|
|
||||||
$migrationNames = [
|
|
||||||
'create_test_table',
|
|
||||||
];
|
|
||||||
foreach ($migrationNames as $migrationName) {
|
|
||||||
$this->assertCommandCreatedFile('create_test', $migrationName);
|
|
||||||
|
|
||||||
$this->assertCommandCreatedFile('create_fields', $migrationName, [
|
|
||||||
'fields' => 'title:string(10):notNull:unique:defaultValue("test"),
|
|
||||||
body:text:notNull,
|
|
||||||
price:money(11,2):notNull,
|
|
||||||
parenthesis_in_comment:string(255):notNull:comment(\'Name of set (RU)\')'
|
|
||||||
]);
|
|
||||||
|
|
||||||
$this->assertCommandCreatedFile('create_title_pk', $migrationName, [
|
|
||||||
'fields' => 'title:primaryKey,body:text:notNull,price:money(11,2)',
|
|
||||||
]);
|
|
||||||
|
|
||||||
$this->assertCommandCreatedFile('create_id_pk', $migrationName, [
|
|
||||||
'fields' => 'id:primaryKey,
|
|
||||||
address:string,
|
|
||||||
address2:string,
|
|
||||||
email:string',
|
|
||||||
]);
|
|
||||||
|
|
||||||
$this->assertCommandCreatedFile('create_foreign_key', $migrationName, [
|
|
||||||
'fields' => 'user_id:integer:foreignKey,
|
|
||||||
product_id:foreignKey:integer:unsigned:notNull,
|
|
||||||
order_id:integer:foreignKey(user_order):notNull,
|
|
||||||
created_at:dateTime:notNull',
|
|
||||||
]);
|
|
||||||
|
|
||||||
$this->assertCommandCreatedFile('create_prefix', $migrationName, [
|
|
||||||
'useTablePrefix' => true,
|
|
||||||
'fields' => 'user_id:integer:foreignKey,
|
|
||||||
product_id:foreignKey:integer:unsigned:notNull,
|
|
||||||
order_id:integer:foreignKey(user_order):notNull,
|
|
||||||
created_at:dateTime:notNull',
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
// @see https://github.com/yiisoft/yii2/issues/10876
|
|
||||||
$this->assertCommandCreatedFile('create_products_from_store_table', 'create_products_from_store_table');
|
|
||||||
|
|
||||||
// @see https://github.com/yiisoft/yii2/issues/11461
|
|
||||||
$this->assertCommandCreatedFile('create_title_with_comma_default_values', 'create_test_table', [
|
|
||||||
'fields' => 'title:string(10):notNull:unique:defaultValue(",te,st"),
|
|
||||||
body:text:notNull:defaultValue(",test"),
|
|
||||||
test:custom(11,2,"s"):notNull',
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testGenerateDropMigration()
|
|
||||||
{
|
|
||||||
$migrationNames = [
|
|
||||||
'drop_test_table',
|
|
||||||
];
|
|
||||||
foreach ($migrationNames as $migrationName) {
|
|
||||||
$this->assertCommandCreatedFile('drop_test', $migrationName);
|
|
||||||
|
|
||||||
$this->assertCommandCreatedFile('drop_fields', $migrationName, [
|
|
||||||
'fields' => 'body:text:notNull,price:money(11,2)'
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
// @see https://github.com/yiisoft/yii2/issues/10876
|
|
||||||
$this->assertCommandCreatedFile('drop_products_from_store_table', 'drop_products_from_store_table');
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testGenerateAddColumnMigration()
|
|
||||||
{
|
|
||||||
$migrationNames = [
|
|
||||||
'add_columns_column_to_test_table',
|
|
||||||
'add_columns_columns_to_test_table',
|
|
||||||
];
|
|
||||||
foreach ($migrationNames as $migrationName) {
|
|
||||||
$this->assertCommandCreatedFile('add_columns_test', $migrationName, [
|
|
||||||
'fields' => 'title:string(10):notNull,
|
|
||||||
body:text:notNull,
|
|
||||||
price:money(11,2):notNull,
|
|
||||||
created_at:dateTime'
|
|
||||||
]);
|
|
||||||
|
|
||||||
$this->assertCommandCreatedFile('add_columns_fk', $migrationName, [
|
|
||||||
'fields' => 'user_id:integer:foreignKey,
|
|
||||||
product_id:foreignKey:integer:unsigned:notNull,
|
|
||||||
order_id:integer:foreignKey(user_order):notNull,
|
|
||||||
created_at:dateTime:notNull',
|
|
||||||
]);
|
|
||||||
|
|
||||||
$this->assertCommandCreatedFile('add_columns_prefix', $migrationName, [
|
|
||||||
'useTablePrefix' => true,
|
|
||||||
'fields' => 'user_id:integer:foreignKey,
|
|
||||||
product_id:foreignKey:integer:unsigned:notNull,
|
|
||||||
order_id:integer:foreignKey(user_order):notNull,
|
|
||||||
created_at:dateTime:notNull',
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testGenerateDropColumnMigration()
|
|
||||||
{
|
|
||||||
$migrationNames = [
|
|
||||||
'drop_columns_column_from_test_table',
|
|
||||||
'drop_columns_columns_from_test_table',
|
|
||||||
];
|
|
||||||
foreach ($migrationNames as $migrationName) {
|
|
||||||
$this->assertCommandCreatedFile('drop_columns_test', $migrationName, [
|
|
||||||
'fields' => 'title:string(10):notNull,body:text:notNull,
|
|
||||||
price:money(11,2):notNull,
|
|
||||||
created_at:dateTime'
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testGenerateCreateJunctionMigration()
|
|
||||||
{
|
|
||||||
$migrationNames = [
|
|
||||||
'create_junction_post_and_tag_tables',
|
|
||||||
'create_junction_for_post_and_tag_tables',
|
|
||||||
'create_junction_table_for_post_and_tag_tables',
|
|
||||||
'create_junction_table_for_post_and_tag_table',
|
|
||||||
];
|
|
||||||
foreach ($migrationNames as $migrationName) {
|
|
||||||
$this->assertCommandCreatedFile('junction_test', $migrationName);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testUp()
|
public function testUp()
|
||||||
{
|
{
|
||||||
@ -329,7 +219,7 @@ CODE;
|
|||||||
|
|
||||||
$this->runMigrateControllerAction('up');
|
$this->runMigrateControllerAction('up');
|
||||||
|
|
||||||
$this->assertMigrationHistory(['base', 'test1', 'test2']);
|
$this->assertMigrationHistory(['m*_base', 'm*_test1', 'm*_test2']);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -342,7 +232,7 @@ CODE;
|
|||||||
|
|
||||||
$this->runMigrateControllerAction('up', [1]);
|
$this->runMigrateControllerAction('up', [1]);
|
||||||
|
|
||||||
$this->assertMigrationHistory(['base', 'test1']);
|
$this->assertMigrationHistory(['m*_base', 'm*_test1']);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -356,7 +246,7 @@ CODE;
|
|||||||
$this->runMigrateControllerAction('up');
|
$this->runMigrateControllerAction('up');
|
||||||
$this->runMigrateControllerAction('down', [1]);
|
$this->runMigrateControllerAction('down', [1]);
|
||||||
|
|
||||||
$this->assertMigrationHistory(['base', 'test1']);
|
$this->assertMigrationHistory(['m*_base', 'm*_test1']);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -370,7 +260,7 @@ CODE;
|
|||||||
$this->runMigrateControllerAction('up');
|
$this->runMigrateControllerAction('up');
|
||||||
$this->runMigrateControllerAction('down', ['all']);
|
$this->runMigrateControllerAction('down', ['all']);
|
||||||
|
|
||||||
$this->assertMigrationHistory(['base']);
|
$this->assertMigrationHistory(['m*_base']);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -413,7 +303,17 @@ CODE;
|
|||||||
|
|
||||||
$this->runMigrateControllerAction('mark', [$version]);
|
$this->runMigrateControllerAction('mark', [$version]);
|
||||||
|
|
||||||
$this->assertMigrationHistory(['base', 'test1']);
|
$this->assertMigrationHistory(['m*_base', 'm*_test1']);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testTo()
|
||||||
|
{
|
||||||
|
$version = '020202_000001';
|
||||||
|
$this->createMigration('to1', $version);
|
||||||
|
|
||||||
|
$this->runMigrateControllerAction('to', [$version]);
|
||||||
|
|
||||||
|
$this->assertMigrationHistory(['m*_base', 'm*_to1']);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -426,6 +326,148 @@ CODE;
|
|||||||
|
|
||||||
$this->runMigrateControllerAction('redo');
|
$this->runMigrateControllerAction('redo');
|
||||||
|
|
||||||
$this->assertMigrationHistory(['base', 'test1']);
|
$this->assertMigrationHistory(['m*_base', 'm*_test1']);
|
||||||
|
}
|
||||||
|
|
||||||
|
// namespace :
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @depends testCreate
|
||||||
|
*/
|
||||||
|
public function testNamespaceCreate()
|
||||||
|
{
|
||||||
|
// default namespace apply :
|
||||||
|
$migrationName = 'testDefaultNamespace';
|
||||||
|
$this->runMigrateControllerAction('create', [$migrationName], [
|
||||||
|
'migrationPath' => null,
|
||||||
|
'migrationNamespaces' => [$this->migrationNamespace]
|
||||||
|
]);
|
||||||
|
$files = FileHelper::findFiles($this->migrationPath);
|
||||||
|
$fileContent = file_get_contents($files[0]);
|
||||||
|
$this->assertContains("namespace {$this->migrationNamespace};", $fileContent);
|
||||||
|
$this->assertRegExp('/class M[0-9]{12}' . ucfirst($migrationName) . '/s', $fileContent);
|
||||||
|
unlink($files[0]);
|
||||||
|
|
||||||
|
// namespace specify :
|
||||||
|
$migrationName = 'test_namespace_specify';
|
||||||
|
$this->runMigrateControllerAction('create', [$this->migrationNamespace . '\\' . $migrationName], [
|
||||||
|
'migrationPath' => $this->migrationPath,
|
||||||
|
'migrationNamespaces' => [$this->migrationNamespace]
|
||||||
|
]);
|
||||||
|
$files = FileHelper::findFiles($this->migrationPath);
|
||||||
|
$fileContent = file_get_contents($files[0]);
|
||||||
|
$this->assertContains("namespace {$this->migrationNamespace};", $fileContent);
|
||||||
|
unlink($files[0]);
|
||||||
|
|
||||||
|
// no namespace:
|
||||||
|
$migrationName = 'test_no_namespace';
|
||||||
|
$this->runMigrateControllerAction('create', [$migrationName], [
|
||||||
|
'migrationPath' => $this->migrationPath,
|
||||||
|
'migrationNamespaces' => [$this->migrationNamespace]
|
||||||
|
]);
|
||||||
|
$files = FileHelper::findFiles($this->migrationPath);
|
||||||
|
$fileContent = file_get_contents($files[0]);
|
||||||
|
$this->assertNotContains("namespace {$this->migrationNamespace};", $fileContent);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @depends testUp
|
||||||
|
*/
|
||||||
|
public function testNamespaceUp()
|
||||||
|
{
|
||||||
|
$this->createNamespaceMigration('nsTest1');
|
||||||
|
$this->createNamespaceMigration('nsTest2');
|
||||||
|
|
||||||
|
$this->runMigrateControllerAction('up', [], [
|
||||||
|
'migrationPath' => null,
|
||||||
|
'migrationNamespaces' => [$this->migrationNamespace]
|
||||||
|
]);
|
||||||
|
|
||||||
|
$this->assertMigrationHistory([
|
||||||
|
'm*_*_base',
|
||||||
|
$this->migrationNamespace . '\\M*NsTest1',
|
||||||
|
$this->migrationNamespace . '\\M*NsTest2',
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @depends testNamespaceUp
|
||||||
|
* @depends testDownCount
|
||||||
|
*/
|
||||||
|
public function testNamespaceDownCount()
|
||||||
|
{
|
||||||
|
$this->createNamespaceMigration('down1');
|
||||||
|
$this->createNamespaceMigration('down2');
|
||||||
|
|
||||||
|
$controllerConfig = [
|
||||||
|
'migrationPath' => null,
|
||||||
|
'migrationNamespaces' => [$this->migrationNamespace]
|
||||||
|
];
|
||||||
|
$this->runMigrateControllerAction('up', [], $controllerConfig);
|
||||||
|
$this->runMigrateControllerAction('down', [1], $controllerConfig);
|
||||||
|
|
||||||
|
$this->assertMigrationHistory([
|
||||||
|
'm*_*_base',
|
||||||
|
$this->migrationNamespace . '\\M*Down1',
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @depends testNamespaceUp
|
||||||
|
* @depends testHistory
|
||||||
|
*/
|
||||||
|
public function testNamespaceHistory()
|
||||||
|
{
|
||||||
|
$controllerConfig = [
|
||||||
|
'migrationPath' => null,
|
||||||
|
'migrationNamespaces' => [$this->migrationNamespace]
|
||||||
|
];
|
||||||
|
|
||||||
|
$output = $this->runMigrateControllerAction('history', [], $controllerConfig);
|
||||||
|
$this->assertContains('No migration', $output);
|
||||||
|
|
||||||
|
$this->createNamespaceMigration('history1');
|
||||||
|
$this->createNamespaceMigration('history2');
|
||||||
|
$this->runMigrateControllerAction('up', [], $controllerConfig);
|
||||||
|
|
||||||
|
$output = $this->runMigrateControllerAction('history', [], $controllerConfig);
|
||||||
|
$this->assertRegExp('/' . preg_quote($this->migrationNamespace) . '.*History1/s', $output);
|
||||||
|
$this->assertRegExp('/' . preg_quote($this->migrationNamespace) . '.*History2/s', $output);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @depends testMark
|
||||||
|
*/
|
||||||
|
public function testNamespaceMark()
|
||||||
|
{
|
||||||
|
$controllerConfig = [
|
||||||
|
'migrationPath' => null,
|
||||||
|
'migrationNamespaces' => [$this->migrationNamespace]
|
||||||
|
];
|
||||||
|
|
||||||
|
$version = '010101000001';
|
||||||
|
$this->createNamespaceMigration('mark1', $version);
|
||||||
|
|
||||||
|
$this->runMigrateControllerAction('mark', [$this->migrationNamespace . '\\M' . $version], $controllerConfig);
|
||||||
|
|
||||||
|
$this->assertMigrationHistory(['m*_base', $this->migrationNamespace . '\\M*Mark1']);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @depends testTo
|
||||||
|
*/
|
||||||
|
public function testNamespaceTo()
|
||||||
|
{
|
||||||
|
$controllerConfig = [
|
||||||
|
'migrationPath' => null,
|
||||||
|
'migrationNamespaces' => [$this->migrationNamespace]
|
||||||
|
];
|
||||||
|
|
||||||
|
$version = '020202000020';
|
||||||
|
$this->createNamespaceMigration('to1', $version);
|
||||||
|
|
||||||
|
$this->runMigrateControllerAction('to', [$this->migrationNamespace . '\\M' . $version], $controllerConfig);
|
||||||
|
|
||||||
|
$this->assertMigrationHistory(['m*_base', $this->migrationNamespace . '\\M*To1']);
|
||||||
}
|
}
|
||||||
}
|
}
|
Reference in New Issue
Block a user