Skip to content

Commit

Permalink
Add migration strategies (#16)
Browse files Browse the repository at this point in the history
  • Loading branch information
msmakouz authored Nov 2, 2023
1 parent 9278175 commit 94cb613
Show file tree
Hide file tree
Showing 37 changed files with 566 additions and 217 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/ci-mssql.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ jobs:
matrix:
include:
- php: '8.1'
extensions: pdo, pdo_sqlsrv-5.10.0beta2
extensions: pdo, pdo_sqlsrv
mssql: 'server:2019-latest'

services:
Expand Down Expand Up @@ -69,4 +69,4 @@ jobs:
- name: Run tests with phpunit without coverage
env:
DB: sqlserver
run: vendor/bin/phpunit tests/Migrations/Driver/SQLServer
run: vendor/bin/phpunit --group driver-sqlserver
2 changes: 1 addition & 1 deletion .github/workflows/ci-mysql.yml
Original file line number Diff line number Diff line change
Expand Up @@ -88,4 +88,4 @@ jobs:
env:
DB: mysql
MYSQL: ${{ matrix.mysql-version }}
run: vendor/bin/phpunit tests/Migrations/Driver/MySQL
run: vendor/bin/phpunit --group driver-mysql
2 changes: 1 addition & 1 deletion .github/workflows/ci-pgsql.yml
Original file line number Diff line number Diff line change
Expand Up @@ -89,4 +89,4 @@ jobs:
env:
DB: postgres
POSTGRES: ${{ matrix.pgsql-version }}
run: vendor/bin/phpunit tests/Migrations/Driver/Postgres
run: vendor/bin/phpunit --group driver-postgres
4 changes: 2 additions & 2 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ jobs:
php-version: ${{ matrix.php-versions }}
coverage: pcov
tools: pecl
extensions: mbstring, pdo, pdo_sqlite, pdo_pgsql, pdo_sqlsrv-5.10.0beta2, pdo_mysql
extensions: mbstring, pdo, pdo_sqlite, pdo_pgsql, pdo_sqlsrv, pdo_mysql
- name: Get Composer Cache Directory
id: composer-cache
run: echo "::set-output name=dir::$(composer config cache-files-dir)"
Expand Down Expand Up @@ -97,4 +97,4 @@ jobs:
env:
DB: sqlite
run: |
vendor/bin/phpunit tests/Migrations/Driver/SQLite
vendor/bin/phpunit --group driver-sqlite
2 changes: 1 addition & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
"php": ">=8.1",
"cycle/database": "^2.4.1",
"cycle/schema-builder": "^2.0",
"cycle/migrations": "^4.0"
"cycle/migrations": "^4.2"
},
"require-dev": {
"cycle/orm": "^2.0",
Expand Down
9 changes: 9 additions & 0 deletions src/Exception/GeneratorException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<?php

declare(strict_types=1);

namespace Cycle\Schema\Generator\Migrations\Exception;

final class GeneratorException extends \Exception
{
}
116 changes: 21 additions & 95 deletions src/GenerateMigrations.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,13 @@

namespace Cycle\Schema\Generator\Migrations;

use Cycle\Schema\Generator\Migrations\Exception\GeneratorException;
use Cycle\Schema\Generator\Migrations\Strategy\GeneratorStrategyInterface;
use Cycle\Schema\Generator\Migrations\Strategy\SingleFileStrategy;
use Cycle\Schema\Generator\SyncTables;
use Cycle\Schema\GeneratorInterface;
use Cycle\Schema\Registry;
use Cycle\Database\Schema\AbstractTable;
use Cycle\Migrations\Atomizer\Atomizer;
use Cycle\Migrations\Atomizer\Renderer;
use Cycle\Migrations\Config\MigrationConfig;
use Cycle\Migrations\RepositoryInterface;

Expand All @@ -19,12 +20,17 @@
*/
class GenerateMigrations implements GeneratorInterface
{
private static int $sec = 0;
private GeneratorStrategyInterface $strategy;

/**
* @param MigrationConfig $migrationConfig deprecated param since v2.2.0.
*/
public function __construct(
private RepositoryInterface $repository,
private MigrationConfig $migrationConfig
private MigrationConfig $migrationConfig,
GeneratorStrategyInterface $strategy = null
) {
$this->strategy = $strategy ?? new SingleFileStrategy($migrationConfig, new NameBasedOnChangesGenerator());
}

public function run(Registry $registry): Registry
Expand All @@ -37,15 +43,12 @@ public function run(Registry $registry): Registry
}

foreach ($databases as $database => $tables) {
$image = $this->generate($database, $tables);
if (!$image) {
// no changes
continue;
}
$class = $image->getClass()->getName();
$name = substr($image->buildFileName(), 0, 128);
foreach ($this->strategy->generate($database, $tables) as $image) {
$class = $image->getClass()->getName();
$name = \substr($image->buildFileName(), 0, 128);

$this->repository->registerMigration($name, $class, (string) $image->getFile());
$this->repository->registerMigration($name, $class, (string) $image->getFile());
}
}

return $registry;
Expand All @@ -55,94 +58,17 @@ public function run(Registry $registry): Registry
* @param AbstractTable[] $tables
*
* @return array [string, FileDeclaration]
*
* @deprecated since v2.2.0
*/
protected function generate(string $database, array $tables): ?MigrationImage
{
$atomizer = new Atomizer(new Renderer());

$reasonable = false;
foreach ($tables as $table) {
if ($table->getComparator()->hasChanges()) {
$reasonable = true;
$atomizer->addTable($table);
}
}

if (!$reasonable) {
return null;
if (!$this->strategy instanceof SingleFileStrategy) {
throw new GeneratorException('Only `SingleFileStrategy` is supported.');
}

$image = new MigrationImage($this->migrationConfig, $database);
$image->setName($this->generateName($atomizer));
$image->fileNamePattern = self::$sec++ . '_{database}_{name}';

$atomizer->declareChanges($image->getClass()->getMethod('up'));
$atomizer->revertChanges($image->getClass()->getMethod('down'));

return $image;
}

private function generateName(Atomizer $atomizer): string
{
$name = [];

foreach ($atomizer->getTables() as $table) {
if ($table->getStatus() === AbstractTable::STATUS_NEW) {
$name[] = 'create_' . $table->getName();
continue;
}

if ($table->getStatus() === AbstractTable::STATUS_DECLARED_DROPPED) {
$name[] = 'drop_' . $table->getName();
continue;
}

if ($table->getComparator()->isRenamed()) {
$name[] = 'rename_' . $table->getInitialName();
continue;
}

$name[] = 'change_' . $table->getName();

$comparator = $table->getComparator();

foreach ($comparator->addedColumns() as $column) {
$name[] = 'add_' . $column->getName();
}

foreach ($comparator->droppedColumns() as $column) {
$name[] = 'rm_' . $column->getName();
}

foreach ($comparator->alteredColumns() as $column) {
$name[] = 'alter_' . $column[0]->getName();
}

foreach ($comparator->addedIndexes() as $index) {
$name[] = 'add_index_' . $index->getName();
}

foreach ($comparator->droppedIndexes() as $index) {
$name[] = 'rm_index_' . $index->getName();
}

foreach ($comparator->alteredIndexes() as $index) {
$name[] = 'alter_index_' . $index[0]->getName();
}

foreach ($comparator->addedForeignKeys() as $fk) {
$name[] = 'add_fk_' . $fk->getName();
}

foreach ($comparator->droppedForeignKeys() as $fk) {
$name[] = 'rm_fk_' . $fk->getName();
}

foreach ($comparator->alteredForeignKeys() as $fk) {
$name[] = 'alter_fk_' . $fk[0]->getName();
}
}
$images = $this->strategy->generate($database, $tables);

return implode('_', $name);
return \array_shift($images);
}
}
75 changes: 75 additions & 0 deletions src/NameBasedOnChangesGenerator.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
<?php

declare(strict_types=1);

namespace Cycle\Schema\Generator\Migrations;

use Cycle\Database\Schema\AbstractTable;
use Cycle\Migrations\Atomizer\Atomizer;

final class NameBasedOnChangesGenerator implements NameGeneratorInterface
{
public function generate(Atomizer $atomizer): string
{
$name = [];

foreach ($atomizer->getTables() as $table) {
if ($table->getStatus() === AbstractTable::STATUS_NEW) {
$name[] = 'create_' . $table->getName();
continue;
}

if ($table->getStatus() === AbstractTable::STATUS_DECLARED_DROPPED) {
$name[] = 'drop_' . $table->getName();
continue;
}

if ($table->getComparator()->isRenamed()) {
$name[] = 'rename_' . $table->getInitialName();
continue;
}

$name[] = 'change_' . $table->getName();

$comparator = $table->getComparator();

foreach ($comparator->addedColumns() as $column) {
$name[] = 'add_' . $column->getName();
}

foreach ($comparator->droppedColumns() as $column) {
$name[] = 'rm_' . $column->getName();
}

foreach ($comparator->alteredColumns() as $column) {
$name[] = 'alter_' . $column[0]->getName();
}

foreach ($comparator->addedIndexes() as $index) {
$name[] = 'add_index_' . $index->getName();
}

foreach ($comparator->droppedIndexes() as $index) {
$name[] = 'rm_index_' . $index->getName();
}

foreach ($comparator->alteredIndexes() as $index) {
$name[] = 'alter_index_' . $index[0]->getName();
}

foreach ($comparator->addedForeignKeys() as $fk) {
$name[] = 'add_fk_' . $fk->getName();
}

foreach ($comparator->droppedForeignKeys() as $fk) {
$name[] = 'rm_fk_' . $fk->getName();
}

foreach ($comparator->alteredForeignKeys() as $fk) {
$name[] = 'alter_fk_' . $fk[0]->getName();
}
}

return \implode('_', $name);
}
}
12 changes: 12 additions & 0 deletions src/NameGeneratorInterface.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<?php

declare(strict_types=1);

namespace Cycle\Schema\Generator\Migrations;

use Cycle\Migrations\Atomizer\Atomizer;

interface NameGeneratorInterface
{
public function generate(Atomizer $atomizer): string;
}
19 changes: 19 additions & 0 deletions src/Strategy/GeneratorStrategyInterface.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<?php

declare(strict_types=1);

namespace Cycle\Schema\Generator\Migrations\Strategy;

use Cycle\Database\Schema\AbstractTable;
use Cycle\Schema\Generator\Migrations\MigrationImage;

interface GeneratorStrategyInterface
{
/**
* @param non-empty-string $database
* @param array<AbstractTable> $tables
*
* @return array<MigrationImage>
*/
public function generate(string $database, array $tables): array;
}
54 changes: 54 additions & 0 deletions src/Strategy/MultipleFilesStrategy.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
<?php

declare(strict_types=1);

namespace Cycle\Schema\Generator\Migrations\Strategy;

use Cycle\Database\Schema\AbstractTable;
use Cycle\Migrations\Atomizer\Atomizer;
use Cycle\Migrations\Atomizer\Renderer;
use Cycle\Migrations\Atomizer\TableSorter;
use Cycle\Migrations\Config\MigrationConfig;
use Cycle\Schema\Generator\Migrations\MigrationImage;
use Cycle\Schema\Generator\Migrations\NameGeneratorInterface;

final class MultipleFilesStrategy implements GeneratorStrategyInterface
{
private static int $sec = 0;

public function __construct(
private readonly MigrationConfig $migrationConfig,
private readonly NameGeneratorInterface $nameGenerator,
private readonly TableSorter $tableSorter = new TableSorter()
) {
}

/**
* @param non-empty-string $database
* @param array<AbstractTable> $tables
*
* @return array<MigrationImage>
*/
public function generate(string $database, array $tables): array
{
$atomizer = new Atomizer(new Renderer());

$images = [];
foreach ($this->tableSorter->sort($tables) as $table) {
if ($table->getComparator()->hasChanges()) {
$atomizer->setTables([$table]);

$image = new MigrationImage($this->migrationConfig, $database);
$image->setName($this->nameGenerator->generate($atomizer));
$image->fileNamePattern = self::$sec++ . '_{database}_{name}';

$atomizer->declareChanges($image->getClass()->getMethod('up'));
$atomizer->revertChanges($image->getClass()->getMethod('down'));

$images[] = $image;
}
}

return $images;
}
}
Loading

0 comments on commit 94cb613

Please sign in to comment.