Skip to content

Commit

Permalink
Merge branch 'migration-order-2x' into 2.x
Browse files Browse the repository at this point in the history
  • Loading branch information
Paweł Brzozowski committed Jul 7, 2019
2 parents 3d8bcf8 + 44625a5 commit b6ba536
Show file tree
Hide file tree
Showing 29 changed files with 970 additions and 148 deletions.
8 changes: 4 additions & 4 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,10 @@ addons:
matrix:
fast_finish: true
include:
- php: 7.0
- php: 5.6
- php: 5.5
- php: 5.4
- php: "7.0"
- php: "5.6"
- php: "5.5"
- php: "5.4"

install:
- phpenv config-rm xdebug.ini || echo "xdebug is not installed"
Expand Down
45 changes: 25 additions & 20 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,23 +15,23 @@ Add the package to your composer.json:

{
"require": {
"bizley/migration": "^3.3"
"bizley/migration": "^3.4"
}
}

and run `composer update` or alternatively run `composer require bizley/migration:^3.3`
and run `composer update` or alternatively run `composer require bizley/migration:^3.4`

## Installation for PHP < 7.1

Add the package to your composer.json:

{
"require": {
"bizley/migration": "^2.6"
"bizley/migration": "^2.7"
}
}

and run `composer update` or alternatively run `composer require bizley/migration:^2.6`
and run `composer update` or alternatively run `composer require bizley/migration:^2.7`

## Configuration

Expand Down Expand Up @@ -77,6 +77,10 @@ The following console command are available:
You can generate multiple migrations for many tables at once by separating the names with a comma:

php yii migration/create table_name1,table_name2,table_name3

Starting from version 3.4/2.7 creating multiple table migrations at once forces the proper migration order based on the
presence of the foreign keys. When tables are cross-referenced the additional foreign keys migration is generated at the
end of default generation.

## Updating migration

Expand All @@ -89,22 +93,23 @@ Starting with yii2-migration v2.0 it is possible to generate updating migration

## Command line parameters

| command | alias | description
|----------------------|:-----:|-----------------------------------------------------------------------------------------------------------------
| `db` | | Application component's ID of the DB connection to use when generating migrations. _default:_ `'db'`
| `migrationPath` | `p` | Directory storing the migration classes. _default:_ `'@app/migrations'`
| `migrationNamespace` | `n` | Namespace in case of generating namespaced migration. _default:_ `null`
| `templateFile` | `F` | Template file for generating create migrations. _default:_ `'@bizley/migration/views/create_migration.php'`
| `templateFileUpdate` | `U` | Template file for generating update migrations. _default:_ `'@bizley/migration/views/update_migration.php'`
| `useTablePrefix` | `P` | Whether the table names generated should consider the `tablePrefix` setting of the DB connection. _default:_ `1`
| `migrationTable` | `t` | Name of the table for keeping applied migration information. _default:_ `'{{%migration}}'`
| `showOnly` | `s` | Whether to only display changes instead of generating update migration. _default:_ `0`
| `generalSchema` | `g` | Whether to use general column schema instead of database specific (see [1] below). _default:_ `1`
| `fixHistory` | `h` | Whether to add migration history entry when migration is generated. _default:_ `0`
| `skipMigrations` | | List of migrations from the history table that should be skipped during the update process (see [2] below). _default:_ `[]`
| `tableOptionsInit` | `O` | String rendered in the create migration template to initialize table options. _default:_ `$tableOptions = null; if ($this->db->driverName === 'mysql') { $tableOptions = 'CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci ENGINE=InnoDB'; }`
| `tableOptions` | `o` | String rendered in the create migration template for table options. _default:_ `$tableOptions`
| `excludeTables` | | List of tables that should be skipped for *-all actions. _default:_ `[]`
| command | alias | description
|--------------------------|:-----:|-----------------------------------------------------------------------------------------------------------------
| `db` | | Application component's ID of the DB connection to use when generating migrations. _default:_ `'db'`
| `migrationPath` | `p` | Directory storing the migration classes. _default:_ `'@app/migrations'`
| `migrationNamespace` | `n` | Namespace in case of generating namespaced migration. _default:_ `null`
| `templateFile` | `F` | Template file for generating create migrations. _default:_ `'@bizley/migration/views/create_migration.php'`
| `templateFileUpdate` | `U` | Template file for generating update migrations. _default:_ `'@bizley/migration/views/update_migration.php'`
| `useTablePrefix` | `P` | Whether the table names generated should consider the `tablePrefix` setting of the DB connection. _default:_ `1`
| `migrationTable` | `t` | Name of the table for keeping applied migration information. _default:_ `'{{%migration}}'`
| `showOnly` | `s` | Whether to only display changes instead of generating update migration. _default:_ `0`
| `generalSchema` | `g` | Whether to use general column schema instead of database specific (see [1] below). _default:_ `1`
| `fixHistory` | `h` | Whether to add migration history entry when migration is generated. _default:_ `0`
| `skipMigrations` | | List of migrations from the history table that should be skipped during the update process (see [2] below). _default:_ `[]`
| `tableOptionsInit` | `O` | String rendered in the create migration template to initialize table options. _default:_ `$tableOptions = null; if ($this->db->driverName === 'mysql') { $tableOptions = 'CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci ENGINE=InnoDB'; }`
| `tableOptions` | `o` | String rendered in the create migration template for table options. _default:_ `$tableOptions`
| `excludeTables` | | List of tables that should be skipped for *-all actions. _default:_ `[]`
| `templateFileForeignKey` | `K` | Template file for generating create foreign keys migrations. _default:_ `'@bizley/migration/views/create_fk_migration.php'`

[1] Remember that with different database types general column schemas may be generated with different length.

Expand Down
141 changes: 141 additions & 0 deletions src/Arranger.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
<?php

namespace bizley\migration;

use yii\base\Component;
use yii\base\InvalidConfigException;
use yii\db\Connection;

/**
* Class Arranger
* @package bizley\migration
* @since 2.7.0
*/
class Arranger extends Component
{
/**
* @var Connection DB connection.
*/
public $db;

/**
* @var array DB tables to be arranged.
*/
public $inputTables = [];

/**
* Checks if DB connection is passed.
* @throws InvalidConfigException
*/
public function init()
{
parent::init();

if (!($this->db instanceof Connection)) {
throw new InvalidConfigException("Parameter 'db' must be an instance of yii\\db\\Connection!");
}
}

/**
* @return array
* @throws InvalidConfigException
*/
public function arrangeNewMigrations()
{
foreach ($this->inputTables as $inputTable) {
$this->addDependency($inputTable);

$generator = new Generator([
'db' => $this->db,
'tableName' => $inputTable,
]);

$tableStructure = $generator->getTable();
foreach ($tableStructure->foreignKeys as $foreignKey) {
$this->addDependency($inputTable, $foreignKey->refTable);
}
}

return $this->arrangeTables($this->_dependency);
}

private $_dependency = [];

/**
* @param string $table
* @param string|null $dependensOnTable
*/
protected function addDependency($table, $dependensOnTable = null)
{
if (!array_key_exists($table, $this->_dependency)) {
$this->_dependency[$table] = [];
}

if ($dependensOnTable) {
$this->_dependency[$table][] = $dependensOnTable;
}
}

/**
* @param array $input
* @return array
*/
public function arrangeTables($input)
{
$output = [];
$checkList = [];
$postLink = [];

$inputCount = count($input);

while ($inputCount > count($output)) {
$done = false;
$lastCheckedName = $lastCheckedDependency = null;

foreach ($input as $name => $dependencies) {
if (array_key_exists($name, $checkList)) {
continue;
}

$resolved = true;

foreach ($dependencies as $dependency) {
if (!array_key_exists($dependency, $checkList)) {
$resolved = false;
$lastCheckedName = $name;
$lastCheckedDependency = $dependency;
break;
}
}

if ($resolved) {
$checkList[$name] = true;
$output[] = $name;

$done = true;
}
}

if (!$done) {
$input[$lastCheckedName] = array_diff($input[$lastCheckedName], [$lastCheckedDependency]);

$redo = $this->arrangeTables($input);
$output = $redo['order'];
$postLinkMerged = array_merge_recursive(
[$lastCheckedName => [$lastCheckedDependency]],
$redo['suppressForeignKeys']
);
$filteredLink = [];
foreach ($postLinkMerged as $name => $dependencies) {
$filteredLink[$name] = array_unique($dependencies);
}
$postLink = $filteredLink;
}
}

return [
'order' => $output,
'suppressForeignKeys' => $postLink,
];
}
}
44 changes: 40 additions & 4 deletions src/Generator.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

namespace bizley\migration;

use bizley\migration\table\ForeignKeyData;
use bizley\migration\table\TableColumn;
use bizley\migration\table\TableColumnFactory;
use bizley\migration\table\TableForeignKey;
Expand All @@ -15,8 +16,10 @@
use yii\base\NotSupportedException;
use yii\base\View;
use yii\db\Connection;
use yii\db\Constraint;
use yii\db\ForeignKeyConstraint;
use yii\db\IndexConstraint;
use yii\db\sqlite\Schema;
use yii\db\TableSchema;
use yii\helpers\ArrayHelper;
use yii\helpers\FileHelper;
Expand Down Expand Up @@ -89,6 +92,12 @@ class Generator extends Component
*/
public $tableOptions;

/**
* @var array
* @since 2.7.0
*/
public $suppressForeignKey = [];

/**
* Checks if DB connection is passed.
* @throws InvalidConfigException
Expand Down Expand Up @@ -125,14 +134,14 @@ protected function getTablePrimaryKey()
$data = [];

if (method_exists($this->db->schema, 'getTablePrimaryKey')) { // requires Yii 2.0.13
/* @var $constraint \yii\db\Constraint */
/* @var $constraint Constraint */
$constraint = $this->db->schema->getTablePrimaryKey($this->tableName, true);
if ($constraint) {
$data = [
'columns' => $constraint->columnNames,
'name' => $constraint->name,
];
} elseif ($this->db->schema instanceof \yii\db\sqlite\Schema) {
} elseif ($this->db->schema instanceof Schema) {
// SQLite bug-case fixed in Yii 2.0.16 https://github.com/yiisoft/yii2/issues/16897

if ($this->tableSchema !== null && $this->tableSchema->primaryKey) {
Expand Down Expand Up @@ -269,13 +278,15 @@ protected function getTableIndexes()
'columns' => $cols
]);
}
} catch (NotSupportedException $exc) {}
} catch (NotSupportedException $exc) {
}
}

return $data;
}

private $_table;
private $_suppressedForeignKeys = [];

/**
* Returns table data
Expand All @@ -286,6 +297,21 @@ public function getTable()
{
if ($this->_table === null) {
$indexes = $this->getTableIndexes();
$foreignKeys = $this->getTableForeignKeys();

foreach ($foreignKeys as $foreignKeyName => $foreignKey) {
if (in_array($foreignKey->refTable, $this->suppressForeignKey, true)) {
$this->_suppressedForeignKeys[] = new ForeignKeyData([
'foreignKey' => $foreignKey,
'table' => new TableStructure([
'name' => $this->tableName,
'usePrefix' => $this->useTablePrefix,
'dbPrefix' => $this->db->tablePrefix,
]),
]);
unset($foreignKeys[$foreignKeyName]);
}
}

$this->_table = new TableStructure([
'name' => $this->tableName,
Expand All @@ -294,7 +320,7 @@ public function getTable()
'usePrefix' => $this->useTablePrefix,
'dbPrefix' => $this->db->tablePrefix,
'primaryKey' => $this->getTablePrimaryKey(),
'foreignKeys' => $this->getTableForeignKeys(),
'foreignKeys' => $foreignKeys,
'indexes' => $indexes,
'tableOptionsInit' => $this->tableOptionsInit,
'tableOptions' => $this->tableOptions,
Expand Down Expand Up @@ -328,4 +354,14 @@ public function generateMigration()
'namespace' => $this->normalizedNamespace
]);
}

/**
* Returns list of foreign keys definitions that were suppressed by the configuration.
* @return array
* @since 2.7.0
*/
public function getSuppressedForeignKeys()
{
return $this->_suppressedForeignKeys;
}
}
Loading

0 comments on commit b6ba536

Please sign in to comment.