Fix #17437: Fixed generating namespaced migrations

This commit is contained in:
Bizley
2019-07-23 16:28:43 +02:00
committed by Alexander Makarov
parent a4a22ae1fb
commit e50a07c30c
29 changed files with 1467 additions and 237 deletions

File diff suppressed because it is too large Load Diff

View File

@ -35,7 +35,7 @@ All these tools are accessible through the command `yii migrate`. In this sectio
how to accomplish various tasks using these tools. You may also get the usage of each tool via the help how to accomplish various tasks using these tools. You may also get the usage of each tool via the help
command `yii help migrate`. command `yii help migrate`.
> Tip: migrations could affect not only database schema but adjust existing data to fit new schema, create RBAC > Tip: Migrations could affect not only database schema but adjust existing data to fit new schema, create RBAC
hierarchy or clean up cache. hierarchy or clean up cache.
> Note: When manipulating data using a migration you may find that using your [Active Record](db-active-record.md) classes > Note: When manipulating data using a migration you may find that using your [Active Record](db-active-record.md) classes
@ -303,7 +303,7 @@ class m150811_220037_create_post_table extends Migration
} }
``` ```
> Note: primary key is added automatically and is named `id` by default. If you want to use another name you may > Note: Primary key is added automatically and is named `id` by default. If you want to use another name you may
> specify it explicitly like `--fields="name:primaryKey"`. > specify it explicitly like `--fields="name:primaryKey"`.
#### Foreign keys #### Foreign keys
@ -690,6 +690,7 @@ Below is the list of all these database accessing methods:
* [[yii\db\Migration::insert()|insert()]]: inserting a single row * [[yii\db\Migration::insert()|insert()]]: inserting a single row
* [[yii\db\Migration::batchInsert()|batchInsert()]]: inserting multiple rows * [[yii\db\Migration::batchInsert()|batchInsert()]]: inserting multiple rows
* [[yii\db\Migration::update()|update()]]: updating rows * [[yii\db\Migration::update()|update()]]: updating rows
* [[yii\db\Migration::upsert()|upsert()]]: inserting a single row or updating it if it exists (since 2.0.14)
* [[yii\db\Migration::delete()|delete()]]: deleting rows * [[yii\db\Migration::delete()|delete()]]: deleting rows
* [[yii\db\Migration::createTable()|createTable()]]: creating a table * [[yii\db\Migration::createTable()|createTable()]]: creating a table
* [[yii\db\Migration::renameTable()|renameTable()]]: renaming a table * [[yii\db\Migration::renameTable()|renameTable()]]: renaming a table
@ -722,7 +723,6 @@ Below is the list of all these database accessing methods:
> } > }
> ``` > ```
## Applying Migrations <span id="applying-migrations"></span> ## Applying Migrations <span id="applying-migrations"></span>
To upgrade a database to its latest structure, you should apply all available new migrations using the following command: To upgrade a database to its latest structure, you should apply all available new migrations using the following command:
@ -801,7 +801,7 @@ yii migrate/redo 3 # redo the last 3 applied migrations
Since Yii 2.0.13 you can delete all tables and foreign keys from the database and apply all migrations from the beginning. Since Yii 2.0.13 you can delete all tables and foreign keys from the database and apply all migrations from the beginning.
``` ```
yii migrate/fresh # Truncate the database and apply all migrations from the beginning. yii migrate/fresh # truncate the database and apply all migrations from the beginning
``` ```
## Listing Migrations <span id="listing-migrations"></span> ## Listing Migrations <span id="listing-migrations"></span>
@ -935,7 +935,7 @@ return [
]; ];
``` ```
> Note: migrations applied from different namespaces will create a **single** migration history, e.g. you might be > Note: Migrations applied from different namespaces will create a **single** migration history, e.g. you might be
unable to apply or revert migrations from particular namespace only. unable to apply or revert migrations from particular namespace only.
While operating namespaced migrations: creating new, reverting and so on, you should specify full namespace before While operating namespaced migrations: creating new, reverting and so on, you should specify full namespace before
@ -943,10 +943,10 @@ migration name. Note that backslash (`\`) symbol is usually considered a special
to escape it properly to avoid shell errors or incorrect behavior. For example: to escape it properly to avoid shell errors or incorrect behavior. For example:
``` ```
yii migrate/create 'app\\migrations\\createUserTable' yii migrate/create app\\migrations\\CreateUserTable
``` ```
> Note: migrations specified via [[yii\console\controllers\MigrateController::migrationPath|migrationPath]] can not > Note: Migrations specified via [[yii\console\controllers\MigrateController::migrationPath|migrationPath]] can not
contain a namespace, namespaced migration can be applied only via [[yii\console\controllers\MigrateController::migrationNamespaces]] contain a namespace, namespaced migration can be applied only via [[yii\console\controllers\MigrateController::migrationNamespaces]]
property. property.
@ -956,6 +956,32 @@ This is mainly added to be used in existing projects which use migrations from d
from external sources, like Yii extensions developed by other developers, from external sources, like Yii extensions developed by other developers,
which can not be changed to use namespaces easily when starting to use the new approach. which can not be changed to use namespaces easily when starting to use the new approach.
#### Generating namespaced migrations
Namespaced migrations follow "CamelCase" naming pattern `M<YYMMDDHHMMSS><Name>` (for example `M190720100234CreateUserTable`).
When generating such migration remember that table name will be converted from "CamelCase" format to "underscored". For
example:
```
yii migrate/create app\\migrations\\DropGreenHotelTable
```
generates migration within namespace `app\migrations` dropping table `green_hotel` and
```
yii migrate/create app\\migrations\\CreateBANANATable
```
generates migration within namespace `app\migrations` creating table `b_a_n_a_n_a`.
If table's name is mixed-cased (like `studentsExam`) you need to precede the name with underscore:
```
yii migrate/create app\\migrations\\Create_studentsExamTable
```
This generates migration within namespace `app\migrations` creating table `studentsExam`.
### Separated Migrations <span id="separated-migrations"></span> ### Separated Migrations <span id="separated-migrations"></span>
Sometimes using single migration history for all project migrations is not desirable. For example: you may install some Sometimes using single migration history for all project migrations is not desirable. For example: you may install some

View File

@ -7,6 +7,7 @@ Yii Framework 2 Change Log
- Bug #17219: Fixed quoting of table names with spaces in MSSQL (alexkart) - Bug #17219: Fixed quoting of table names with spaces in MSSQL (alexkart)
- Bug #10020: Fixed quoting of column names with dots in MSSQL (alexkart) - Bug #10020: Fixed quoting of column names with dots in MSSQL (alexkart)
- Bug #17424: Subdomain support for `User::loginRequired` (alex-code) - Bug #17424: Subdomain support for `User::loginRequired` (alex-code)
- Bug #17437: Fixed generating namespaced migrations (bizley)
2.0.23 July 16, 2019 2.0.23 July 16, 2019

View File

@ -17,6 +17,7 @@ use yii\console\ExitCode;
use yii\db\MigrationInterface; use yii\db\MigrationInterface;
use yii\helpers\Console; use yii\helpers\Console;
use yii\helpers\FileHelper; use yii\helpers\FileHelper;
use yii\helpers\Inflector;
/** /**
* BaseMigrateController is the base class for migrate controllers. * BaseMigrateController is the base class for migrate controllers.
@ -661,17 +662,15 @@ abstract class BaseMigrateController extends Controller
if (strpos($name, '\\') !== false) { if (strpos($name, '\\') !== false) {
$namespace = substr($name, 0, strrpos($name, '\\')); $namespace = substr($name, 0, strrpos($name, '\\'));
$name = substr($name, strrpos($name, '\\') + 1); $name = substr($name, strrpos($name, '\\') + 1);
} else { } elseif ($this->migrationPath === null) {
if ($this->migrationPath === null) { $migrationNamespaces = $this->migrationNamespaces;
$migrationNamespaces = $this->migrationNamespaces; $namespace = array_shift($migrationNamespaces);
$namespace = array_shift($migrationNamespaces);
}
} }
if ($namespace === null) { if ($namespace === null) {
$class = 'm' . gmdate('ymd_His') . '_' . $name; $class = 'm' . gmdate('ymd_His') . '_' . $name;
} else { } else {
$class = 'M' . gmdate('ymdHis') . ucfirst($name); $class = 'M' . gmdate('ymdHis') . Inflector::camelize($name);
} }
return [$namespace, $class]; return [$namespace, $class];

View File

@ -13,6 +13,7 @@ use yii\db\Query;
use yii\di\Instance; use yii\di\Instance;
use yii\helpers\ArrayHelper; use yii\helpers\ArrayHelper;
use yii\helpers\Console; use yii\helpers\Console;
use yii\helpers\Inflector;
/** /**
* Manages application migrations. * Manages application migrations.
@ -375,6 +376,26 @@ class MigrateController extends BaseMigrateController
return static::MAX_NAME_LENGTH; return static::MAX_NAME_LENGTH;
} }
/**
* Normalizes table name for generator.
* When name is preceded with underscore name case is kept - otherwise it's converted from camelcase to underscored.
* Last underscore is always trimmed so if there should be underscore at the end of name use two of them.
* @param string $name
* @return string
*/
private function normalizeTableName($name)
{
if (substr($name, -1) === '_') {
$name = substr($name, 0, -1);
}
if (strpos($name, '_') === 0) {
return substr($name, 1);
}
return Inflector::underscore($name);
}
/** /**
* {@inheritdoc} * {@inheritdoc}
* @since 2.0.8 * @since 2.0.8
@ -386,13 +407,20 @@ class MigrateController extends BaseMigrateController
$foreignKeys = $parsedFields['foreignKeys']; $foreignKeys = $parsedFields['foreignKeys'];
$name = $params['name']; $name = $params['name'];
if ($params['namespace']) {
$name = substr($name, strrpos($name, '\\') + 1);
}
$templateFile = $this->templateFile; $templateFile = $this->templateFile;
$table = null; $table = null;
if (preg_match('/^create_junction(?:_table_for_|_for_|_)(.+)_and_(.+)_tables?$/', $name, $matches)) { if (preg_match(
'/^create_?junction_?(?:table)?_?(?:for)?(.+)_?and(.+)_?tables?$/i',
$name,
$matches
)) {
$templateFile = $this->generatorTemplateFiles['create_junction']; $templateFile = $this->generatorTemplateFiles['create_junction'];
$firstTable = $matches[1]; $firstTable = $this->normalizeTableName($matches[1]);
$secondTable = $matches[2]; $secondTable = $this->normalizeTableName($matches[2]);
$fields = array_merge( $fields = array_merge(
[ [
@ -420,20 +448,20 @@ class MigrateController extends BaseMigrateController
$foreignKeys[$firstTable . '_id']['column'] = null; $foreignKeys[$firstTable . '_id']['column'] = null;
$foreignKeys[$secondTable . '_id']['column'] = null; $foreignKeys[$secondTable . '_id']['column'] = null;
$table = $firstTable . '_' . $secondTable; $table = $firstTable . '_' . $secondTable;
} elseif (preg_match('/^add_(.+)_columns?_to_(.+)_table$/', $name, $matches)) { } elseif (preg_match('/^add(.+)columns?_?to(.+)table$/i', $name, $matches)) {
$templateFile = $this->generatorTemplateFiles['add_column']; $templateFile = $this->generatorTemplateFiles['add_column'];
$table = $matches[2]; $table = $this->normalizeTableName($matches[2]);
} elseif (preg_match('/^drop_(.+)_columns?_from_(.+)_table$/', $name, $matches)) { } elseif (preg_match('/^drop(.+)columns?_?from(.+)table$/i', $name, $matches)) {
$templateFile = $this->generatorTemplateFiles['drop_column']; $templateFile = $this->generatorTemplateFiles['drop_column'];
$table = $matches[2]; $table = $this->normalizeTableName($matches[2]);
} elseif (preg_match('/^create_(.+)_table$/', $name, $matches)) { } elseif (preg_match('/^create(.+)table$/i', $name, $matches)) {
$this->addDefaultPrimaryKey($fields); $this->addDefaultPrimaryKey($fields);
$templateFile = $this->generatorTemplateFiles['create_table']; $templateFile = $this->generatorTemplateFiles['create_table'];
$table = $matches[1]; $table = $this->normalizeTableName($matches[1]);
} elseif (preg_match('/^drop_(.+)_table$/', $name, $matches)) { } elseif (preg_match('/^drop(.+)table$/i', $name, $matches)) {
$this->addDefaultPrimaryKey($fields); $this->addDefaultPrimaryKey($fields);
$templateFile = $this->generatorTemplateFiles['drop_table']; $templateFile = $this->generatorTemplateFiles['drop_table'];
$table = $matches[1]; $table = $this->normalizeTableName($matches[1]);
} }
foreach ($foreignKeys as $column => $foreignKey) { foreach ($foreignKeys as $column => $foreignKey) {

View File

@ -9,9 +9,6 @@
/* @var $table string the name table */ /* @var $table string the name table */
/* @var $fields array the fields */ /* @var $fields array the fields */
preg_match('/^add_(.+)_columns?_to_(.+)_table$/', $name, $matches);
$columns = str_replace('_column_', ', ', $matches[1]);
echo "<?php\n"; echo "<?php\n";
if (!empty($namespace)) { if (!empty($namespace)) {
echo "\nnamespace {$namespace};\n"; echo "\nnamespace {$namespace};\n";
@ -21,7 +18,7 @@ if (!empty($namespace)) {
use yii\db\Migration; use yii\db\Migration;
/** /**
* Handles adding <?= $columns ?> to table `<?= $table ?>`. * Handles adding columns to table `<?= $table ?>`.
<?= $this->render('_foreignTables', [ <?= $this->render('_foreignTables', [
'foreignKeys' => $foreignKeys, 'foreignKeys' => $foreignKeys,
]) ?> ]) ?>

View File

@ -8,8 +8,6 @@
/* @var $namespace string the new migration class 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);
$columns = $matches[1];
echo "<?php\n"; echo "<?php\n";
if (!empty($namespace)) { if (!empty($namespace)) {
@ -20,7 +18,7 @@ if (!empty($namespace)) {
use yii\db\Migration; use yii\db\Migration;
/** /**
* Handles dropping <?= $columns ?> from table `<?= $table ?>`. * Handles dropping columns from table `<?= $table ?>`.
<?= $this->render('_foreignTables', [ <?= $this->render('_foreignTables', [
'foreignKeys' => $foreignKeys, 'foreignKeys' => $foreignKeys,
]) ?> ]) ?>

View File

@ -8,7 +8,7 @@
return <<<CODE return <<<CODE
<?php <?php
use yii\db\Migration; {$namespace}use yii\db\Migration;
/** /**
* Handles adding columns to table `{{%{table}}}`. * Handles adding columns to table `{{%{table}}}`.

View File

@ -8,7 +8,7 @@
return <<<CODE return <<<CODE
<?php <?php
use yii\db\Migration; {$namespace}use yii\db\Migration;
/** /**
* Handles adding columns to table `{{%{table}}}`. * Handles adding columns to table `{{%{table}}}`.

View File

@ -8,7 +8,7 @@
return <<<CODE return <<<CODE
<?php <?php
use yii\db\Migration; {$namespace}use yii\db\Migration;
/** /**
* Handles adding columns to table `{{%{table}}}`. * Handles adding columns to table `{{%{table}}}`.

View File

@ -8,10 +8,10 @@
return <<<CODE return <<<CODE
<?php <?php
use yii\db\Migration; {$namespace}use yii\db\Migration;
/** /**
* Handles adding field_1, field_2 to table `{{%{table}}}`. * Handles adding columns to table `{{%{table}}}`.
*/ */
class {$class} extends Migration class {$class} extends Migration
{ {

View File

@ -8,7 +8,7 @@
return <<<CODE return <<<CODE
<?php <?php
use yii\db\Migration; {$namespace}use yii\db\Migration;
/** /**
* Handles the creation of table `{{%test}}`. * Handles the creation of table `{{%test}}`.

View File

@ -8,7 +8,7 @@
return <<<CODE return <<<CODE
<?php <?php
use yii\db\Migration; {$namespace}use yii\db\Migration;
/** /**
* Handles the creation of table `{{%{table}}}`. * Handles the creation of table `{{%{table}}}`.

View File

@ -8,7 +8,7 @@
return <<<CODE return <<<CODE
<?php <?php
use yii\db\Migration; {$namespace}use yii\db\Migration;
/** /**
* Handles the creation of table `{{%{table}}}`. * Handles the creation of table `{{%{table}}}`.

View File

@ -8,7 +8,7 @@
return <<<CODE return <<<CODE
<?php <?php
use yii\db\Migration; {$namespace}use yii\db\Migration;
/** /**
* Handles the creation of table `{{%{table}}}`. * Handles the creation of table `{{%{table}}}`.

View File

@ -8,7 +8,7 @@
return <<<CODE return <<<CODE
<?php <?php
use yii\db\Migration; {$namespace}use yii\db\Migration;
/** /**
* Handles the creation of table `{{%{table}}}`. * Handles the creation of table `{{%{table}}}`.

View File

@ -8,7 +8,7 @@
return <<<CODE return <<<CODE
<?php <?php
use yii\db\Migration; {$namespace}use yii\db\Migration;
/** /**
* Handles the creation of table `{{%{table}}}`. * Handles the creation of table `{{%{table}}}`.

View File

@ -8,7 +8,7 @@
return <<<CODE return <<<CODE
<?php <?php
use yii\db\Migration; {$namespace}use yii\db\Migration;
/** /**
* Handles the creation of table `{{%{table}}}`. * Handles the creation of table `{{%{table}}}`.

View File

@ -8,7 +8,7 @@
return <<<CODE return <<<CODE
<?php <?php
use yii\db\Migration; {$namespace}use yii\db\Migration;
/** /**
* Handles the creation of table `{{%test}}`. * Handles the creation of table `{{%test}}`.

View File

@ -8,7 +8,7 @@
return <<<CODE return <<<CODE
<?php <?php
use yii\db\Migration; {$namespace}use yii\db\Migration;
/** /**
* Handles the creation of table `{{%{table}}}`. * Handles the creation of table `{{%{table}}}`.

View File

@ -8,7 +8,7 @@
return <<<CODE return <<<CODE
<?php <?php
use yii\db\Migration; {$namespace}use yii\db\Migration;
/** /**
* Handles the creation of table `{{%{table}}}`. * Handles the creation of table `{{%{table}}}`.

View File

@ -8,7 +8,7 @@
return <<<CODE return <<<CODE
<?php <?php
use yii\db\Migration; {$namespace}use yii\db\Migration;
/** /**
* Class {$class} * Class {$class}

View File

@ -8,7 +8,7 @@
return <<<CODE return <<<CODE
<?php <?php
use yii\db\Migration; {$namespace}use yii\db\Migration;
/** /**
* Handles dropping columns from table `{{%{table}}}`. * Handles dropping columns from table `{{%{table}}}`.

View File

@ -8,7 +8,7 @@
return <<<CODE return <<<CODE
<?php <?php
use yii\db\Migration; {$namespace}use yii\db\Migration;
/** /**
* Handles the dropping of table `{{%{table}}}`. * Handles the dropping of table `{{%{table}}}`.

View File

@ -8,7 +8,7 @@
return <<<CODE return <<<CODE
<?php <?php
use yii\db\Migration; {$namespace}use yii\db\Migration;
/** /**
* Handles the dropping of table `{{%{table}}}`. * Handles the dropping of table `{{%{table}}}`.

View File

@ -8,7 +8,7 @@
return <<<CODE return <<<CODE
<?php <?php
use yii\db\Migration; {$namespace}use yii\db\Migration;
/** /**
* Handles the dropping of table `{{%{table}}}`. * Handles the dropping of table `{{%{table}}}`.

View File

@ -8,14 +8,14 @@
return <<<CODE return <<<CODE
<?php <?php
use yii\db\Migration; {$namespace}use yii\db\Migration;
/** /**
* Handles the creation of table `{{%post_tag}}`. * Handles the creation of table `{{%{junctionTable}}}`.
* Has foreign keys to the tables: * Has foreign keys to the tables:
* *
* - `{{%post}}` * - `{{%{firstTable}}}`
* - `{{%tag}}` * - `{{%{secondTable}}}`
*/ */
class {$class} extends Migration class {$class} extends Migration
{ {
@ -24,42 +24,42 @@ class {$class} extends Migration
*/ */
public function safeUp() public function safeUp()
{ {
\$this->createTable('{{%post_tag}}', [ \$this->createTable('{{%{junctionTable}}}', [
'post_id' => \$this->integer(), '{firstTable}_id' => \$this->integer(),
'tag_id' => \$this->integer(), '{secondTable}_id' => \$this->integer(),
'PRIMARY KEY(post_id, tag_id)', 'PRIMARY KEY({firstTable}_id, {secondTable}_id)',
]); ]);
// creates index for column `post_id` // creates index for column `{firstTable}_id`
\$this->createIndex( \$this->createIndex(
'{{%idx-post_tag-post_id}}', '{{%idx-{junctionTable}-{firstTable}_id}}',
'{{%post_tag}}', '{{%{junctionTable}}}',
'post_id' '{firstTable}_id'
); );
// add foreign key for table `{{%post}}` // add foreign key for table `{{%{firstTable}}}`
\$this->addForeignKey( \$this->addForeignKey(
'{{%fk-post_tag-post_id}}', '{{%fk-{junctionTable}-{firstTable}_id}}',
'{{%post_tag}}', '{{%{junctionTable}}}',
'post_id', '{firstTable}_id',
'{{%post}}', '{{%{firstTable}}}',
'id', 'id',
'CASCADE' 'CASCADE'
); );
// creates index for column `tag_id` // creates index for column `{secondTable}_id`
\$this->createIndex( \$this->createIndex(
'{{%idx-post_tag-tag_id}}', '{{%idx-{junctionTable}-{secondTable}_id}}',
'{{%post_tag}}', '{{%{junctionTable}}}',
'tag_id' '{secondTable}_id'
); );
// add foreign key for table `{{%tag}}` // add foreign key for table `{{%{secondTable}}}`
\$this->addForeignKey( \$this->addForeignKey(
'{{%fk-post_tag-tag_id}}', '{{%fk-{junctionTable}-{secondTable}_id}}',
'{{%post_tag}}', '{{%{junctionTable}}}',
'tag_id', '{secondTable}_id',
'{{%tag}}', '{{%{secondTable}}}',
'id', 'id',
'CASCADE' 'CASCADE'
); );
@ -70,31 +70,31 @@ class {$class} extends Migration
*/ */
public function safeDown() public function safeDown()
{ {
// drops foreign key for table `{{%post}}` // drops foreign key for table `{{%{firstTable}}}`
\$this->dropForeignKey( \$this->dropForeignKey(
'{{%fk-post_tag-post_id}}', '{{%fk-{junctionTable}-{firstTable}_id}}',
'{{%post_tag}}' '{{%{junctionTable}}}'
); );
// drops index for column `post_id` // drops index for column `{firstTable}_id`
\$this->dropIndex( \$this->dropIndex(
'{{%idx-post_tag-post_id}}', '{{%idx-{junctionTable}-{firstTable}_id}}',
'{{%post_tag}}' '{{%{junctionTable}}}'
); );
// drops foreign key for table `{{%tag}}` // drops foreign key for table `{{%{secondTable}}}`
\$this->dropForeignKey( \$this->dropForeignKey(
'{{%fk-post_tag-tag_id}}', '{{%fk-{junctionTable}-{secondTable}_id}}',
'{{%post_tag}}' '{{%{junctionTable}}}'
); );
// drops index for column `tag_id` // drops index for column `{secondTable}_id`
\$this->dropIndex( \$this->dropIndex(
'{{%idx-post_tag-tag_id}}', '{{%idx-{junctionTable}-{secondTable}_id}}',
'{{%post_tag}}' '{{%{junctionTable}}}'
); );
\$this->dropTable('{{%post_tag}}'); \$this->dropTable('{{%{junctionTable}}}');
} }
} }

View File

@ -11,6 +11,7 @@ use Yii;
use yii\console\controllers\MigrateController; use yii\console\controllers\MigrateController;
use yii\db\Migration; use yii\db\Migration;
use yii\db\Query; use yii\db\Query;
use yii\helpers\Inflector;
use yiiunit\TestCase; use yiiunit\TestCase;
/** /**
@ -57,96 +58,300 @@ class MigrateControllerTest extends TestCase
return $query->from('migration')->all(); return $query->from('migration')->all();
} }
public function assertFileContent($expectedFile, $class, $table) public function assertFileContent($expectedFile, $class, $table, $namespace = null)
{ {
if ($namespace) {
$namespace = "namespace {$namespace};\n\n";
}
$expected = include Yii::getAlias("@yiiunit/data/console/migrate_create/$expectedFile.php"); $expected = include Yii::getAlias("@yiiunit/data/console/migrate_create/$expectedFile.php");
$expected = str_replace('{table}', $table, $expected); $expected = str_replace('{table}', $table, $expected);
$this->assertEqualsWithoutLE($expected, $this->parseNameClassMigration($class, $table)); $this->assertEqualsWithoutLE($expected, $this->parseNameClassMigration($class));
} }
protected function assertCommandCreatedFile($expectedFile, $migrationName, $table, $params = []) protected function assertCommandCreatedFile($expectedFile, $migrationName, $table, $params = [])
{ {
$class = 'm' . gmdate('ymd_His') . '_' . $migrationName;
$params[0] = $migrationName; $params[0] = $migrationName;
$this->runMigrateControllerAction('create', $params); list($config, $namespace, $class) = $this->prepareMigrationNameData($migrationName);
$this->assertFileContent($expectedFile, $class, $table);
$this->runMigrateControllerAction('create', $params, $config);
$this->assertFileContent($expectedFile, $class, $table, $namespace);
} }
// Tests : public function assertFileContentJunction($expectedFile, $class, $junctionTable, $firstTable, $secondTable, $namespace = null)
public function testGenerateDefaultMigration()
{ {
$this->assertCommandCreatedFile('default', 'DefaultTest', 'default'); if ($namespace) {
$namespace = "namespace {$namespace};\n\n";
}
$expected = include Yii::getAlias("@yiiunit/data/console/migrate_create/$expectedFile.php");
$expected = str_replace(
['{junctionTable}', '{firstTable}', '{secondTable}'],
[$junctionTable, $firstTable, $secondTable],
$expected
);
$this->assertEqualsWithoutLE($expected, $this->parseNameClassMigration($class));
} }
public function testGenerateCreateMigration() protected function assertCommandCreatedJunctionFile($expectedFile, $migrationName, $junctionTable, $firstTable, $secondTable)
{ {
$tables = [ list($config, $namespace, $class) = $this->prepareMigrationNameData($migrationName);
'test',
'TEST',
];
foreach ($tables as $table) {
$migrationName = 'create_' . $table . '_table';
$this->assertCommandCreatedFile('create_test', $migrationName, $table); $this->runMigrateControllerAction('create', [$migrationName], $config);
$this->assertFileContentJunction($expectedFile, $class, $junctionTable, $firstTable, $secondTable, $namespace);
}
$this->assertCommandCreatedFile('create_fields', $migrationName, $table, [ protected function prepareMigrationNameData($migrationName)
{
$config = [];
$namespace = null;
$lastSlashPosition = strrpos($migrationName, '\\');
if ($lastSlashPosition !== false) {
$config = [
'migrationPath' => null,
'migrationNamespaces' => [$this->migrationNamespace],
];
$class = 'M' . gmdate('ymdHis') . Inflector::camelize(substr($migrationName, $lastSlashPosition + 1));
$namespace = substr($migrationName, 0, $lastSlashPosition);
} else {
$class = 'm' . gmdate('ymd_His') . '_' . $migrationName;
}
return [$config, $namespace, $class];
}
/**
* @return array
*/
public function generateMigrationDataProvider()
{
$params = [
'create_fields' => [
'fields' => 'title:string(10):notNull:unique:defaultValue("test"), 'fields' => 'title:string(10):notNull:unique:defaultValue("test"),
body:text:notNull, body:text:notNull,
price:money(11,2):notNull, price:money(11,2):notNull,
parenthesis_in_comment:string(255):notNull:comment(\'Name of set (RU)\')', parenthesis_in_comment:string(255):notNull:comment(\'Name of set (RU)\')',
]); ],
'create_title_pk' => [
$this->assertCommandCreatedFile('create_title_pk', $migrationName, $table, [
'fields' => 'title:primaryKey,body:text:notNull,price:money(11,2)', 'fields' => 'title:primaryKey,body:text:notNull,price:money(11,2)',
]); ],
'create_unsigned_pk' => [
$this->assertCommandCreatedFile('create_unsigned_pk', $migrationName, $table, [
'fields' => 'brand_id:primaryKey:unsigned', 'fields' => 'brand_id:primaryKey:unsigned',
]); ],
'create_unsigned_big_pk' => [
$this->assertCommandCreatedFile('create_unsigned_big_pk', $migrationName, $table, [
'fields' => 'brand_id:bigPrimaryKey:unsigned', 'fields' => 'brand_id:bigPrimaryKey:unsigned',
]); ],
'create_id_pk' => [
$this->assertCommandCreatedFile('create_id_pk', $migrationName, $table, [
'fields' => 'id:primaryKey, 'fields' => 'id:primaryKey,
address:string, address:string,
address2:string, address2:string,
email:string', email:string',
]); ],
'create_foreign_key' => [
$this->assertCommandCreatedFile('create_foreign_key', $migrationName, $table, [
'fields' => 'user_id:integer:foreignKey, 'fields' => 'user_id:integer:foreignKey,
product_id:foreignKey:integer:unsigned:notNull, product_id:foreignKey:integer:unsigned:notNull,
order_id:integer:foreignKey(user_order):notNull, order_id:integer:foreignKey(user_order):notNull,
created_at:dateTime:notNull', created_at:dateTime:notNull',
]); ],
'create_prefix' => [
$this->assertCommandCreatedFile('create_prefix', $migrationName, $table, [
'useTablePrefix' => true, 'useTablePrefix' => true,
'fields' => 'user_id:integer:foreignKey, 'fields' => 'user_id:integer:foreignKey,
product_id:foreignKey:integer:unsigned:notNull, product_id:foreignKey:integer:unsigned:notNull,
order_id:integer:foreignKey(user_order):notNull, order_id:integer:foreignKey(user_order):notNull,
created_at:dateTime:notNull', created_at:dateTime:notNull',
]); ],
} 'create_title_with_comma_default_values' => [
'fields' => 'title:string(10):notNull:unique:defaultValue(",te,st"),
body:text:notNull:defaultValue(",test"),
test:custom(11,2,"s"):notNull',
],
'create_field_with_colon_default_values' => [
'fields' => 'field_1:dateTime:notNull:defaultValue(\'0000-00-00 00:00:00\'),
field_2:string:defaultValue(\'default:value\')',
],
'drop_fields' => [
'fields' => 'body:text:notNull,price:money(11,2)',
],
'add_columns_test' => [
'fields' => 'title:string(10):notNull,
body:text:notNull,
price:money(11,2):notNull,
created_at:dateTime',
],
'add_columns_fk' => [
'fields' => 'user_id:integer:foreignKey,
product_id:foreignKey:integer:unsigned:notNull,
order_id:integer:foreignKey(user_order):notNull,
created_at:dateTime:notNull',
],
'add_columns_prefix' => [
'useTablePrefix' => true,
'fields' => 'user_id:integer:foreignKey,
product_id:foreignKey:integer:unsigned:notNull,
order_id:integer:foreignKey(user_order):notNull,
created_at:dateTime:notNull',
],
'add_two_columns_test' => [
'fields' => 'field_1:string(10):notNull,
field_2:text:notNull',
],
'drop_columns_test' => [
'fields' => 'title:string(10):notNull,body:text:notNull,
price:money(11,2):notNull,
created_at:dateTime',
],
];
// @see https://github.com/yiisoft/yii2/issues/10876 return [
foreach (['products_from_store', 'products_FROM_store'] as $table) { ['default', 'DefaultTest', 'default', []],
$this->assertCommandCreatedFile('drop_products_from_store_table', 'drop_' . $table . '_table', $table);
}
// @see https://github.com/yiisoft/yii2/issues/11461
$this->assertCommandCreatedFile('create_title_with_comma_default_values', 'create_test_table', 'test', [
'fields' => 'title:string(10):notNull:unique:defaultValue(",te,st"),
body:text:notNull:defaultValue(",test"),
test:custom(11,2,"s"):notNull',
]);
$this->assertCommandCreatedFile('create_field_with_colon_default_values', 'create_test_table', 'test', [ // underscore + table name = case keeped
'fields' => 'field_1:dateTime:notNull:defaultValue(\'0000-00-00 00:00:00\'), ['create_test', 'create_test_table', 'test', []],
field_2:string:defaultValue(\'default:value\')', ['create_test', 'create_test__table', 'test_', []],
]); ['create_test', 'create_TEST_table', 'TEST', []],
['create_test', 'Create_tEsTTable', 'tEsT', []],
// no underscore + table name = camelcase converted to underscore
['create_test', 'CreateTestTable', 'test', []],
['create_test', 'createTest_table', 'test', []],
['create_test', 'createTe_st_table', 'te_st', []],
['create_test', 'createTest__table', 'test_', []],
['create_test', 'createTESTtable', 't_e_s_t', []],
['create_fields', 'create_test_table', 'test', $params['create_fields']],
['create_fields', 'create_TEST_table', 'TEST', $params['create_fields']],
['create_title_pk', 'create_test_table', 'test', $params['create_title_pk']],
['create_title_pk', 'create_TEST_table', 'TEST', $params['create_title_pk']],
['create_unsigned_pk', 'create_test_table', 'test', $params['create_unsigned_pk']],
['create_unsigned_pk', 'create_TEST_table', 'TEST', $params['create_unsigned_pk']],
['create_unsigned_big_pk', 'create_test_table', 'test', $params['create_unsigned_big_pk']],
['create_unsigned_big_pk', 'create_TEST_table', 'TEST', $params['create_unsigned_big_pk']],
['create_id_pk', 'create_test_table', 'test', $params['create_id_pk']],
['create_id_pk', 'create_TEST_table', 'TEST', $params['create_id_pk']],
['create_foreign_key', 'create_test_table', 'test', $params['create_foreign_key']],
['create_foreign_key', 'create_TEST_table', 'TEST', $params['create_foreign_key']],
['create_prefix', 'create_test_table', 'test', $params['create_prefix']],
['create_prefix', 'create_TEST_table', 'TEST', $params['create_prefix']],
// @see https://github.com/yiisoft/yii2/issues/11461
['create_title_with_comma_default_values', 'create_test_table', 'test', $params['create_title_with_comma_default_values']],
['create_field_with_colon_default_values', 'create_test_table', 'test', $params['create_field_with_colon_default_values']],
['drop_test', 'drop_test_table', 'test', []],
['drop_test', 'drop_test__table', 'test_', []],
['drop_test', 'drop_TEST_table', 'TEST', []],
['drop_test', 'Drop_tEStTable', 'tESt', []],
['drop_test', 'DropTestTable', 'test', []],
['drop_test', 'DropTest_Table', 'test', []],
['drop_test', 'DropTest__Table', 'test_', []],
['drop_test', 'DropTESTtable', 't_e_s_t', []],
['drop_fields', 'drop_test_table', 'test', $params['drop_fields']],
['drop_fields', 'drop_TEST_table', 'TEST', $params['drop_fields']],
// @see https://github.com/yiisoft/yii2/issues/10876
['drop_products_from_store_table', 'drop_products_from_store_table', 'products_from_store', []],
['drop_products_from_store_table', 'drop_products_FROM_store_table', 'products_FROM_store', []],
['add_columns_test', 'add_columns_column_to_test_table', 'test', $params['add_columns_test']],
['add_columns_test', 'add_columns_column_to_test__table', 'test_', $params['add_columns_test']],
['add_columns_test', 'add_columns_column_to_TEST_table', 'TEST', $params['add_columns_test']],
['add_columns_test', 'AddColumns_column_to_teSTtable', 'teST', $params['add_columns_test']],
['add_columns_test', 'AddColumnsColumnTo_tEstTable', 'tEst', $params['add_columns_test']],
['add_columns_test', 'addColumnsColumnToTestTable', 'test', $params['add_columns_test']],
['add_columns_test', 'AddColumnsColumnToTest_Table', 'test', $params['add_columns_test']],
['add_columns_test', 'AddCol__umnsColumnToTest__Table', 'test_', $params['add_columns_test']],
['add_columns_test', 'AddColumnsColumnToTESTTable', 't_e_s_t', $params['add_columns_test']],
['add_columns_fk', 'add_columns_column_to_test_table', 'test', $params['add_columns_fk']],
['add_columns_fk', 'add_columns_column_to_TEST_table', 'TEST', $params['add_columns_fk']],
['add_columns_prefix', 'add_columns_column_to_test_table', 'test', $params['add_columns_prefix']],
['add_columns_prefix', 'add_columns_column_to_TEST_table', 'TEST', $params['add_columns_prefix']],
['add_two_columns_test', 'add_field_1_column_field_2_column_to_test_table', 'test', $params['add_two_columns_test']],
['add_two_columns_test', 'add_field_1_column_field_2_column_to_TEST_table', 'TEST', $params['add_two_columns_test']],
['drop_columns_test', 'drop_columns_column_from_test_table', 'test', $params['add_columns_test']],
['drop_columns_test', 'drop_columns_columns_from_test_table', 'test', $params['add_columns_test']],
['drop_columns_test', 'drop_columns_column_from_test__table', 'test_', $params['add_columns_test']],
['drop_columns_test', 'drop_columns_column_from_TEST_table', 'TEST', $params['add_columns_test']],
['drop_columns_test', 'drop_columns_columns_from_TEST_table', 'TEST', $params['add_columns_test']],
['drop_columns_test', 'dropColumnsColumNSFrom_TEstTable', 'TEst', $params['add_columns_test']],
['drop_columns_test', 'DropFewColumnsFrom_Test_Table', 'Test', $params['add_columns_test']],
['drop_columns_test', 'DropFewColumnsFromTestTable', 'test', $params['add_columns_test']],
['drop_columns_test', 'DropFewColumnsFromTest_Table', 'test', $params['add_columns_test']],
['drop_columns_test', 'DropFewColumnsFromTest__Table', 'test_', $params['add_columns_test']],
['drop_columns_test', 'DropFewColumnsFromTeStTable', 'te_st', $params['add_columns_test']],
];
}
/**
* @param string $expectedFile
* @param string $migrationName
* @param string $table
* @param array $params
* @dataProvider generateMigrationDataProvider
*/
public function testGenerateMigration($expectedFile, $migrationName, $table, $params)
{
$this->migrationNamespace = 'yiiunit\runtime\test_migrations';
$this->assertCommandCreatedFile($expectedFile, $migrationName, $table, $params);
$this->assertCommandCreatedFile(
$expectedFile,
$this->migrationNamespace . '\\' . $migrationName,
$table,
$params
);
}
/**
* @return array
*/
public function generateJunctionMigrationDataProvider()
{
return [
['create_junction_post_and_tag_tables', 'post_tag', 'post', 'tag'],
['create_junction_for_post_and_tag_tables', 'post_tag', 'post', 'tag'],
['create_junction_table_for_post_and_tag_tables', 'post_tag', 'post', 'tag'],
['create_junction_table_for_post_and_tag_table', 'post_tag', 'post', 'tag'],
['CreateJunction_postAnd_tagTables', 'post_tag', 'post', 'tag'],
['CreateJunctionFor_postAnd_tagTables', 'post_tag', 'post', 'tag'],
['CreateJunctionTableFor_postAnd_tagTables', 'post_tag', 'post', 'tag'],
['CreateJunctionTableFor_postAnd_tagTable', 'post_tag', 'post', 'tag'],
['CreateJunctionPostAndTagTables', 'post_tag', 'post', 'tag'],
['CreateJunctionPost_AndTag_Tables', 'post_tag', 'post', 'tag'],
['CreateJunctionPost__AndTag__Tables', 'post__tag_', 'post_', 'tag_'],
['CreateJunctionPost__AndTagTables', 'post__tag', 'post_', 'tag'],
['CreateJunctionPostAndTag__Tables', 'post_tag_', 'post', 'tag_'],
['CreateJunctionPostAndTaGTables', 'post_ta_g', 'post', 'ta_g'],
['CreateJunctionPoStAndTagTables', 'po_st_tag', 'po_st', 'tag'],
];
}
/**
* @param string $migrationName
* @param string $junctionTable
* @param string $firstTable
* @param string $secondTable
* @dataProvider generateJunctionMigrationDataProvider
*/
public function testGenerateJunctionMigration($migrationName, $junctionTable, $firstTable, $secondTable)
{
$this->migrationNamespace = 'yiiunit\runtime\test_migrations';
$this->assertCommandCreatedJunctionFile(
'junction_test',
$migrationName,
$junctionTable,
$firstTable,
$secondTable
);
$this->assertCommandCreatedJunctionFile(
'junction_test',
$this->migrationNamespace . '\\' . $migrationName,
$junctionTable,
$firstTable,
$secondTable
);
} }
public function testUpdatingLongNamedMigration() public function testUpdatingLongNamedMigration()
@ -190,100 +395,6 @@ class MigrateControllerTest extends TestCase
$controller->run('create', $params); $controller->run('create', $params);
} }
public function testGenerateDropMigration()
{
$tables = [
'test',
'TEST',
];
foreach ($tables as $table) {
$migrationName = 'drop_' . $table . '_table';
$this->assertCommandCreatedFile('drop_test', $migrationName, $table);
$this->assertCommandCreatedFile('drop_fields', $migrationName, $table, [
'fields' => 'body:text:notNull,price:money(11,2)',
]);
}
// @see https://github.com/yiisoft/yii2/issues/10876
foreach (['products_from_store', 'products_FROM_store'] as $table) {
$this->assertCommandCreatedFile('drop_products_from_store_table', 'drop_' . $table . '_table', $table);
}
}
public function testGenerateAddColumnMigration()
{
$tables = [
'test',
'TEST',
];
foreach ($tables as $table) {
$migrationName = 'add_columns_column_to_' . $table . '_table';
$this->assertCommandCreatedFile('add_columns_test', $migrationName, $table, [
'fields' => 'title:string(10):notNull,
body:text:notNull,
price:money(11,2):notNull,
created_at:dateTime',
]);
$this->assertCommandCreatedFile('add_columns_fk', $migrationName, $table, [
'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, $table, [
'useTablePrefix' => true,
'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_two_columns_test', 'add_field_1_column_field_2_column_to_' . $table . '_table', $table, [
'fields' => 'field_1:string(10):notNull,
field_2:text:notNull',
]);
}
}
public function testGenerateDropColumnMigration()
{
$tables = [
'test',
'TEST',
];
foreach ($tables as $table) {
$migrationNames = [
'drop_columns_column_from_' . $table . '_table',
'drop_columns_columns_from_' . $table . '_table',
];
foreach ($migrationNames as $migrationName) {
$this->assertCommandCreatedFile('drop_columns_test', $migrationName, $table, [
'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, 'post_tag');
}
}
/** /**
* Test the migrate:fresh command. * Test the migrate:fresh command.
* @dataProvider refreshMigrationDataProvider * @dataProvider refreshMigrationDataProvider

View File

@ -172,7 +172,7 @@ CODE;
{ {
$files = FileHelper::findFiles($this->migrationPath); $files = FileHelper::findFiles($this->migrationPath);
$file = file_get_contents($files[0]); $file = file_get_contents($files[0]);
if (preg_match('/class (m\d+_\d+_.*) extends Migration/', $file, $match)) { if (preg_match('/class (m\d+_?\d+_?.*) extends Migration/i', $file, $match)) {
$file = str_replace($match[1], $class, $file); $file = str_replace($match[1], $class, $file);
} }
$this->tearDownMigrationPath(); $this->tearDownMigrationPath();