Skip to content

Commit

Permalink
Merge pull request #152 from bizley/migration-timestamps
Browse files Browse the repository at this point in the history
4.3.0
  • Loading branch information
Bizley authored Oct 16, 2021
2 parents f8eefee + fead242 commit 8e79918
Show file tree
Hide file tree
Showing 26 changed files with 867 additions and 279 deletions.
19 changes: 10 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,32 +64,32 @@ The following console command are available (assuming you named the controller `
- Generate migration to create DB table `table_name`:

```
php yii migration/create table_name
php yii migration/create "table_name"
```

- Generate migration to update DB table `table_name`:

```
php yii migration/update table_name
php yii migration/update "table_name"
```

To generate migrations for all the tables in the database at once (except the excluded ones) use asterisk (*):

```
php yii migration/create *
php yii migration/update *
php yii migration/create "*"
php yii migration/update "*"
```
In environments that hijack asterisk (like dockerized env) use `"*"`.

You can generate multiple migrations for many tables at once by separating the names with comma:

```
php yii migration/create table_name1,table_name2,table_name3
php yii migration/create "table_name1,table_name2,table_name3"
```

You can provide an asterisk as a part of table name to use all tables matching the pattern:

```
php yii migration/update prefix_*
php yii migration/update "prefix_*"
```

Creating multiple table migrations at once forces the proper migration order based on the presence of the foreign keys.
Expand Down Expand Up @@ -118,8 +118,9 @@ You can easily generate updating migration for database table by comparing its c
| `skipMigrations` | | List of migrations from the history table that should be skipped during the update process (see [2] below).
| `excludeTables` | | List of tables that should be skipped.
| `experimental` | `ex` | Whether to run in experimental mode (see [3] below).
| `fileMode` | `fm` | **New in 4.2.0** - Generated file mode to be changed using `chmod`.
| `fileOwnership` | `fo` | **New in 4.2.0** - Generated file ownership to be changed using `chown`/`chgrp`.
| `fileMode` | `fm` | Generated file mode to be changed using `chmod`.
| `fileOwnership` | `fo` | Generated file ownership to be changed using `chown`/`chgrp`.
| `leeway` | `lw` | **New in 4.3.0** - The leeway in seconds to apply to a starting timestamp when generating migration, so it can be saved with a later date

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

Expand Down
2 changes: 1 addition & 1 deletion infection.json.dist
Original file line number Diff line number Diff line change
Expand Up @@ -19,5 +19,5 @@
"mutators": {
"@default": true
},
"minCoveredMsi": 95
"minCoveredMsi": 97
}
4 changes: 2 additions & 2 deletions migrating_to_v4.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ pattern, except excluded ones).

### create-all

Not available anymore. Use `create *` instead (or `create "*"` in environments that hijack asterisk).
Not available anymore. Use `create "*"` instead.

### update

Expand All @@ -87,4 +87,4 @@ pattern, except excluded ones).

### update-all

Not available anymore. Use `update *` instead (or `update "*"` in environments that hijack asterisk).
Not available anymore. Use `update "*"` instead.
2 changes: 1 addition & 1 deletion src/Arranger.php
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ public function arrangeTables(array $inputTables): void
foreach ($inputTables as $inputTable) {
$this->addDependency($inputTable);
$foreignKeys = $this->mapper->getStructureOf($inputTable)->getForeignKeys();
/** @var ForeignKeyInterface $foreignKey */

foreach ($foreignKeys as $foreignKey) {
$this->addDependency($inputTable, $foreignKey->getReferredTable());
}
Expand Down
37 changes: 7 additions & 30 deletions src/Comparator.php
Original file line number Diff line number Diff line change
Expand Up @@ -240,15 +240,10 @@ private function compareForeignKeys(
$oldForeignKey = $oldStructure->getForeignKey($name);
$newForeignKeyColumns = $foreignKey->getColumns();
$oldForeignKeyColumns = $oldForeignKey->getColumns();
$intersection = array_intersect($newForeignKeyColumns, $oldForeignKeyColumns);

if (
count(
array_merge(
array_diff($newForeignKeyColumns, $intersection),
array_diff($oldForeignKeyColumns, $intersection)
)
)
array_diff($newForeignKeyColumns, $oldForeignKeyColumns)
!== array_diff($oldForeignKeyColumns, $newForeignKeyColumns)
) {
$blueprint->addDescription(
"different foreign key '$name' columns ("
Expand All @@ -273,15 +268,10 @@ private function compareForeignKeys(

$newForeignKeyReferredColumns = $foreignKey->getReferredColumns();
$oldForeignKeyReferredColumns = $oldForeignKey->getReferredColumns();
$intersection = array_intersect($newForeignKeyReferredColumns, $oldForeignKeyReferredColumns);

if (
count(
array_merge(
array_diff($newForeignKeyReferredColumns, $intersection),
array_diff($oldForeignKeyReferredColumns, $intersection)
)
)
array_diff($newForeignKeyReferredColumns, $oldForeignKeyReferredColumns)
!== array_diff($oldForeignKeyReferredColumns, $newForeignKeyReferredColumns)
) {
$blueprint->addDescription(
"different foreign key '$name' referred columns ("
Expand Down Expand Up @@ -408,14 +398,9 @@ private function comparePrimaryKeys(

$newPrimaryKeyColumns = $newPrimaryKey ? $newPrimaryKey->getColumns() : [];
$oldPrimaryKeyColumns = $oldPrimaryKey ? $oldPrimaryKey->getColumns() : [];
$intersection = array_intersect($newPrimaryKeyColumns, $oldPrimaryKeyColumns);
$differentColumns = array_diff($newPrimaryKeyColumns, $oldPrimaryKeyColumns);

$differentColumns = array_merge(
array_diff($newPrimaryKeyColumns, $intersection),
array_diff($oldPrimaryKeyColumns, $intersection)
);

if (count($differentColumns)) {
if ($differentColumns !== array_diff($oldPrimaryKeyColumns, $newPrimaryKeyColumns)) {
$blueprint->addDescription('different primary key definition');

$alreadyDropped = false;
Expand Down Expand Up @@ -570,16 +555,8 @@ private function compareIndexes(

$newIndexColumns = $index->getColumns();
$oldIndexColumns = $oldIndex->getColumns();
$intersection = array_intersect($newIndexColumns, $oldIndexColumns);

if (
count(
array_merge(
array_diff($newIndexColumns, $intersection),
array_diff($oldIndexColumns, $intersection)
)
)
) {
if (array_diff($newIndexColumns, $oldIndexColumns) !== array_diff($oldIndexColumns, $newIndexColumns)) {
$blueprint->addDescription(
"different index '$name' columns (DB: "
. $this->stringifyValue($newIndexColumns) . ') != MIG: ('
Expand Down
49 changes: 29 additions & 20 deletions src/Extractor.php
Original file line number Diff line number Diff line change
Expand Up @@ -37,31 +37,13 @@ public function __construct(Connection $db, bool $experimental = false)
/**
* Extracts migration data structures.
* @param string $migration
* @param array<string> $migrationPaths
* @param string[] $migrationPaths
* @throws ErrorException
*/
public function extract(string $migration, array $migrationPaths): void
{
$this->setDummyMigrationClass();

if (strpos($migration, '\\') === false) { // not namespaced
$fileFound = false;
$file = null;
foreach ($migrationPaths as $path) {
/** @var string $file */
$file = Yii::getAlias($path . DIRECTORY_SEPARATOR . $migration . '.php');
if (file_exists($file)) {
$fileFound = true;
break;
}
}

if (!$fileFound) {
throw new ErrorException("File '{$migration}.php' can not be found! Check migration history table.");
}

require_once $file;
}
$this->loadFile($migration, $migrationPaths);

$this->subject = new $migration(['db' => $this->db, 'experimental' => $this->experimental]);
if ($this->subject instanceof MigrationChangesInterface === false) {
Expand All @@ -73,6 +55,32 @@ public function extract(string $migration, array $migrationPaths): void
$this->subject->up();
}

/**
* Loads a non-namespaced file.
* @param string $migration
* @param string[] $migrationPaths
* @throws ErrorException
*/
private function loadFile(string $migration, array $migrationPaths): void
{
if (strpos($migration, '\\') !== false) {
// migration with `\` character is most likely namespaced, so it doesn't require loading
return;
}

foreach ($migrationPaths as $path) {
/** @var string $file */
$file = Yii::getAlias($path . DIRECTORY_SEPARATOR . $migration . '.php');
if (file_exists($file)) {
require_once $file;

return;
}
}

throw new ErrorException("File '{$migration}.php' can not be found! Check migration history table.");
}

/**
* Sets the dummy migration file instead the real one to extract the migration structure instead of applying them.
* It uses Yii's class map autoloaders hack.
Expand All @@ -82,6 +90,7 @@ private function setDummyMigrationClass(): void
{
// attempt to register Yii's autoloader in case it's not been done already
// registering it second time should be skipped anyway
/** @infection-ignore-all */
spl_autoload_register(['Yii', 'autoload'], true, true);

Yii::$classMap['yii\db\Migration'] = Yii::getAlias('@bizley/migration/dummy/Migration.php');
Expand Down
4 changes: 2 additions & 2 deletions src/HistoryManager.php
Original file line number Diff line number Diff line change
Expand Up @@ -108,12 +108,12 @@ public function fetchHistory(): array
->all($this->db);

$history = [];
foreach ($rows as $key => $row) {
foreach ($rows as $row) {
if ($row['version'] === MigrateController::BASE_MIGRATION) {
continue;
}

if (preg_match('/m?(\d{6}_?\d{6})(\D.*)?$/is', $row['version'], $matches)) {
if (preg_match('/m?(\d{6}_?\d{6})(\D.*)?/i', $row['version'], $matches)) {
$row['canonicalVersion'] = str_replace('_', '', $matches[1]);
} else {
$row['canonicalVersion'] = $row['version'];
Expand Down
6 changes: 3 additions & 3 deletions src/Schema.php
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,7 @@ public static function identifySchema($schema): string
*/
public static function isSQLite($schema): bool
{
return static::identifySchema($schema) === self::SQLITE;
return self::identifySchema($schema) === self::SQLITE;
}

/**
Expand All @@ -190,7 +190,7 @@ public static function getDefaultLength(?string $schema, string $type, string $e
$schema = self::MYSQL . '+';
}

return static::$defaultLength[$schema][$type] ?? null;
return self::$defaultLength[$schema][$type] ?? null;
}

/**
Expand All @@ -206,6 +206,6 @@ public static function getAlias(?string $schema, string $type, string $length):
return null;
}

return static::$aliases[$schema][$type][$length] ?? null;
return self::$aliases[$schema][$type][$length] ?? null;
}
}
4 changes: 2 additions & 2 deletions src/SqlColumnMapper.php
Original file line number Diff line number Diff line change
Expand Up @@ -232,7 +232,7 @@ private function detectUnique(): void
* @param int $offset
* @return array{0: int, 1: string}
*/
private function findPart(string $type, string $sentence, int $offset = 0): array
private function findPart(string $type, string $sentence, int $offset): array
{
$sentence = substr($sentence, $offset);

Expand All @@ -257,7 +257,7 @@ private function findPart(string $type, string $sentence, int $offset = 0): arra
[$end, $part] = $this->findExpressionPart($sentenceArray);
}

return [$end ? $end + $offset : 0, $part];
return [$end + $offset, $part];
}

/**
Expand Down
31 changes: 20 additions & 11 deletions src/TableMapper.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
use PDO;
use Throwable;
use yii\base\NotSupportedException;
use yii\db\ColumnSchema;
use yii\db\Connection;
use yii\db\Constraint;
use yii\db\cubrid\Schema as CubridSchema;
Expand Down Expand Up @@ -54,7 +55,7 @@ public function getStructureOf(string $table, array $referencesToPostpone = []):
{
$this->suppressedForeignKeys = [];
$foreignKeys = $this->getForeignKeys($table);
/** @var ForeignKeyInterface $foreignKey */

foreach ($foreignKeys as $foreignKeyName => $foreignKey) {
if (in_array($foreignKey->getReferredTable(), $referencesToPostpone, true)) {
$this->suppressedForeignKeys[] = $foreignKey;
Expand Down Expand Up @@ -171,16 +172,7 @@ private function getColumns(string $table, array $indexes = []): array
$engineVersion = $this->getEngineVersion();

foreach ($tableSchema->columns as $column) {
$isUnique = false;

/** @var IndexInterface $index */
foreach ($indexes as $index) {
$indexColumns = $index->getColumns();
if ($index->isUnique() && count($indexColumns) === 1 && $indexColumns[0] === $column->name) {
$isUnique = true;
break;
}
}
$isUnique = $this->isUnique($indexes, $column);

$mappedColumn = ColumnFactory::build($column->type);
$mappedColumn->setName($column->name);
Expand Down Expand Up @@ -209,6 +201,23 @@ private function getColumns(string $table, array $indexes = []): array
return $mappedColumns;
}

/**
* @param array<IndexInterface> $indexes
* @param ColumnSchema $column
* @return bool
*/
private function isUnique(array $indexes, ColumnSchema $column): bool
{
foreach ($indexes as $index) {
$indexColumns = $index->getColumns();
if ($index->isUnique() && count($indexColumns) === 1 && $indexColumns[0] === $column->name) {
return true;
}
}

return false;
}

/**
* Returns the suppressed foreign keys that must be added in migration at the end.
* @return array<ForeignKeyInterface>
Expand Down
Loading

0 comments on commit 8e79918

Please sign in to comment.