Browse Source

Fix #17437: Fixed generating namespaced migrations

tags/2.0.24
Bizley 5 years ago committed by Alexander Makarov
parent
commit
e50a07c30c
  1. 1070
      docs/guide-pl/db-migrations.md
  2. 40
      docs/guide/db-migrations.md
  3. 1
      framework/CHANGELOG.md
  4. 11
      framework/console/controllers/BaseMigrateController.php
  5. 50
      framework/console/controllers/MigrateController.php
  6. 5
      framework/views/addColumnMigration.php
  7. 4
      framework/views/dropColumnMigration.php
  8. 2
      tests/data/console/migrate_create/add_columns_fk.php
  9. 2
      tests/data/console/migrate_create/add_columns_prefix.php
  10. 2
      tests/data/console/migrate_create/add_columns_test.php
  11. 4
      tests/data/console/migrate_create/add_two_columns_test.php
  12. 2
      tests/data/console/migrate_create/create_field_with_colon_default_values.php
  13. 2
      tests/data/console/migrate_create/create_fields.php
  14. 2
      tests/data/console/migrate_create/create_foreign_key.php
  15. 2
      tests/data/console/migrate_create/create_id_pk.php
  16. 2
      tests/data/console/migrate_create/create_prefix.php
  17. 2
      tests/data/console/migrate_create/create_test.php
  18. 2
      tests/data/console/migrate_create/create_title_pk.php
  19. 2
      tests/data/console/migrate_create/create_title_with_comma_default_values.php
  20. 2
      tests/data/console/migrate_create/create_unsigned_big_pk.php
  21. 2
      tests/data/console/migrate_create/create_unsigned_pk.php
  22. 2
      tests/data/console/migrate_create/default.php
  23. 2
      tests/data/console/migrate_create/drop_columns_test.php
  24. 2
      tests/data/console/migrate_create/drop_fields.php
  25. 2
      tests/data/console/migrate_create/drop_products_from_store_table.php
  26. 2
      tests/data/console/migrate_create/drop_test.php
  27. 78
      tests/data/console/migrate_create/junction_test.php
  28. 401
      tests/framework/console/controllers/MigrateControllerTest.php
  29. 2
      tests/framework/console/controllers/MigrateControllerTestTrait.php

1070
docs/guide-pl/db-migrations.md

File diff suppressed because it is too large Load Diff

40
docs/guide/db-migrations.md

@ -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

1
framework/CHANGELOG.md

@ -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

11
framework/console/controllers/BaseMigrateController.php

@ -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];

50
framework/console/controllers/MigrateController.php

@ -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.
@ -376,6 +377,26 @@ class MigrateController extends BaseMigrateController
} }
/** /**
* 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) {

5
framework/views/addColumnMigration.php

@ -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,
]) ?> ]) ?>

4
framework/views/dropColumnMigration.php

@ -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,
]) ?> ]) ?>

2
tests/data/console/migrate_create/add_columns_fk.php

@ -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}}}`.

2
tests/data/console/migrate_create/add_columns_prefix.php

@ -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}}}`.

2
tests/data/console/migrate_create/add_columns_test.php

@ -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}}}`.

4
tests/data/console/migrate_create/add_two_columns_test.php

@ -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
{ {

2
tests/data/console/migrate_create/create_field_with_colon_default_values.php

@ -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}}`.

2
tests/data/console/migrate_create/create_fields.php

@ -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}}}`.

2
tests/data/console/migrate_create/create_foreign_key.php

@ -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}}}`.

2
tests/data/console/migrate_create/create_id_pk.php

@ -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}}}`.

2
tests/data/console/migrate_create/create_prefix.php

@ -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}}}`.

2
tests/data/console/migrate_create/create_test.php

@ -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}}}`.

2
tests/data/console/migrate_create/create_title_pk.php

@ -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}}}`.

2
tests/data/console/migrate_create/create_title_with_comma_default_values.php

@ -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}}`.

2
tests/data/console/migrate_create/create_unsigned_big_pk.php

@ -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}}}`.

2
tests/data/console/migrate_create/create_unsigned_pk.php

@ -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}}}`.

2
tests/data/console/migrate_create/default.php

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

2
tests/data/console/migrate_create/drop_columns_test.php

@ -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}}}`.

2
tests/data/console/migrate_create/drop_fields.php

@ -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}}}`.

2
tests/data/console/migrate_create/drop_products_from_store_table.php

@ -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}}}`.

2
tests/data/console/migrate_create/drop_test.php

@ -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}}}`.

78
tests/data/console/migrate_create/junction_test.php

@ -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}}}');
} }
} }

401
tests/framework/console/controllers/MigrateControllerTest.php

@ -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)
{
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 testGenerateDefaultMigration() protected function assertCommandCreatedJunctionFile($expectedFile, $migrationName, $junctionTable, $firstTable, $secondTable)
{ {
$this->assertCommandCreatedFile('default', 'DefaultTest', 'default'); list($config, $namespace, $class) = $this->prepareMigrationNameData($migrationName);
$this->runMigrateControllerAction('create', [$migrationName], $config);
$this->assertFileContentJunction($expectedFile, $class, $junctionTable, $firstTable, $secondTable, $namespace);
} }
public function testGenerateCreateMigration() protected function prepareMigrationNameData($migrationName)
{ {
$tables = [ $config = [];
'test', $namespace = null;
'TEST',
]; $lastSlashPosition = strrpos($migrationName, '\\');
foreach ($tables as $table) { if ($lastSlashPosition !== false) {
$migrationName = 'create_' . $table . '_table'; $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;
}
$this->assertCommandCreatedFile('create_test', $migrationName, $table); return [$config, $namespace, $class];
}
$this->assertCommandCreatedFile('create_fields', $migrationName, $table, [ /**
* @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);
} // underscore + table name = case keeped
// @see https://github.com/yiisoft/yii2/issues/11461 ['create_test', 'create_test_table', 'test', []],
$this->assertCommandCreatedFile('create_title_with_comma_default_values', 'create_test_table', 'test', [ ['create_test', 'create_test__table', 'test_', []],
'fields' => 'title:string(10):notNull:unique:defaultValue(",te,st"), ['create_test', 'create_TEST_table', 'TEST', []],
body:text:notNull:defaultValue(",test"), ['create_test', 'Create_tEsTTable', 'tEsT', []],
test:custom(11,2,"s"):notNull',
]); // 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']],
];
}
$this->assertCommandCreatedFile('create_field_with_colon_default_values', 'create_test_table', 'test', [ /**
'fields' => 'field_1:dateTime:notNull:defaultValue(\'0000-00-00 00:00:00\'), * @param string $expectedFile
field_2:string:defaultValue(\'default:value\')', * @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

2
tests/framework/console/controllers/MigrateControllerTestTrait.php

@ -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();

Loading…
Cancel
Save