Skip to content

Commit

Permalink
Merge pull request #72 from cycle/feature/show-changes
Browse files Browse the repository at this point in the history
Add PrintChanges generator
  • Loading branch information
msmakouz authored Jan 10, 2024
2 parents 6cbdc76 + 397a351 commit 67da7e7
Show file tree
Hide file tree
Showing 10 changed files with 473 additions and 4 deletions.
3 changes: 2 additions & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@
"require-dev": {
"phpunit/phpunit": "^9.5",
"spiral/tokenizer": "^2.8",
"vimeo/psalm": "^5.12"
"vimeo/psalm": "^5.12",
"symfony/console": "^6.0 || ^7.0"
},
"autoload": {
"psr-4": {
Expand Down
167 changes: 167 additions & 0 deletions src/Generator/PrintChanges.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
<?php

declare(strict_types=1);

namespace Cycle\Schema\Generator;

use Cycle\Database\Schema\ComparatorInterface;
use Cycle\Schema\GeneratorInterface;
use Cycle\Schema\Registry;
use Cycle\Database\Schema\AbstractTable;
use Symfony\Component\Console\Output\OutputInterface;

final class PrintChanges implements GeneratorInterface
{
private array $changes = [];

public function __construct(
private OutputInterface $output,
) {
}

public function run(Registry $registry): Registry
{
$this->changes = [];
foreach ($registry->getIterator() as $e) {
if ($registry->hasTable($e)) {
$table = $registry->getTableSchema($e);

if ($this->hasTableChanges($table)) {
$key = $registry->getDatabase($e) . ':' . $registry->getTable($e);
$this->changes[$key] = [
'database' => $registry->getDatabase($e),
'table' => $registry->getTable($e),
'schema' => $table,
];
}
}
}

if (!$this->hasChanges()) {
$this->output->writeln('<fg=yellow>No database changes has been detected</fg=yellow>');

return $registry;
}

$this->output->writeln('<info>Schema changes:</info>');

foreach ($this->changes as $change) {
$this->output->write(\sprintf('• <fg=cyan>%s.%s</fg=cyan>', $change['database'], $change['table']));
$this->describeChanges($change['schema']);
}

return $registry;
}

public function hasChanges(): bool
{
return $this->changes !== [];
}

private function describeChanges(AbstractTable $table): void
{
if ($table->getStatus() === AbstractTable::STATUS_DECLARED_DROPPED) {
$this->output->writeln(' - drop table');
return;
}

if (!$this->output->isVerbose()) {
$this->output->writeln(
\sprintf(
': <fg=green>%s</fg=green> change(s) detected',
$this->numChanges($table),
),
);

return;
}

$this->output->write("\n");

if (!$table->exists()) {
$this->output->writeln(' - create table');
}

$cmp = $table->getComparator();

$this->describeColumns($cmp);
$this->describeIndexes($cmp);
$this->describeFKs($cmp);
}

private function describeColumns(ComparatorInterface $cmp): void
{
foreach ($cmp->addedColumns() as $column) {
$this->output->writeln(" - add column <fg=yellow>[{$column->getName()}]</fg=yellow>");
}

foreach ($cmp->droppedColumns() as $column) {
$this->output->writeln(" - drop column <fg=yellow>[{$column->getName()}]</fg=yellow>");
}

foreach ($cmp->alteredColumns() as $column) {
$column = $column[0];
$this->output->writeln(" - alter column <fg=yellow>[{$column->getName()}]</fg=yellow>");
}
}

private function describeIndexes(ComparatorInterface $cmp): void
{
foreach ($cmp->addedIndexes() as $index) {
$index = \implode(', ', $index->getColumns());
$this->output->writeln(" - add index on <fg=yellow>[{$index}]</fg=yellow>");
}

foreach ($cmp->droppedIndexes() as $index) {
$index = \implode(', ', $index->getColumns());
$this->output->writeln(" - drop index on <fg=yellow>[{$index}]</fg=yellow>");
}

foreach ($cmp->alteredIndexes() as $index) {
$index = $index[0];
$index = \implode(', ', $index->getColumns());
$this->output->writeln(" - alter index on <fg=yellow>[{$index}]</fg=yellow>");
}
}

private function describeFKs(ComparatorInterface $cmp): void
{
foreach ($cmp->addedForeignKeys() as $fk) {
$fkColumns = \implode(', ', $fk->getColumns());
$this->output->writeln(" - add foreign key on <fg=yellow>[{$fkColumns}]</fg=yellow>");
}

foreach ($cmp->droppedForeignKeys() as $fk) {
$fkColumns = \implode(', ', $fk->getColumns());
$this->output->writeln(" - drop foreign key on <fg=yellow>[{$fkColumns}]</fg=yellow>");
}

foreach ($cmp->alteredForeignKeys() as $fk) {
$fk = $fk[0];
$fkColumns = \implode(', ', $fk->getColumns());
$this->output->writeln(" - alter foreign key on <fg=yellow>[{$fkColumns}]</fg=yellow>");
}
}

private function numChanges(AbstractTable $table): int
{
$cmp = $table->getComparator();

return \count($cmp->addedColumns())
+ \count($cmp->droppedColumns())
+ \count($cmp->alteredColumns())
+ \count($cmp->addedIndexes())
+ \count($cmp->droppedIndexes())
+ \count($cmp->alteredIndexes())
+ \count($cmp->addedForeignKeys())
+ \count($cmp->droppedForeignKeys())
+ \count($cmp->alteredForeignKeys());
}

private function hasTableChanges(AbstractTable $table): bool
{
return $table->getComparator()->hasChanges()
|| !$table->exists()
|| $table->getStatus() === AbstractTable::STATUS_DECLARED_DROPPED;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

use Cycle\Schema\Tests\Generator\ForeignKeysTest as BaseTest;

final class ForeignKeyTest extends BaseTest
final class ForeignKeysTest extends BaseTest
{
public const DRIVER = 'mysql';
}
12 changes: 12 additions & 0 deletions tests/Schema/Driver/MySQL/PrintChangesTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<?php

declare(strict_types=1);

namespace Cycle\Schema\Tests\Driver\MySQL;

use Cycle\Schema\Tests\Generator\PrintChangesTest as BaseTest;

final class PrintChangesTest extends BaseTest
{
public const DRIVER = 'mysql';
}
12 changes: 12 additions & 0 deletions tests/Schema/Driver/Postgres/PrintChangesTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<?php

declare(strict_types=1);

namespace Cycle\Schema\Tests\Driver\Postgres;

use Cycle\Schema\Tests\Generator\PrintChangesTest as BaseTest;

final class PrintChangesTest extends BaseTest
{
public const DRIVER = 'postgres';
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

use Cycle\Schema\Tests\Generator\ForeignKeysTest as BaseTest;

final class ForeignKeyTest extends BaseTest
final class ForeignKeysTest extends BaseTest
{
public const DRIVER = 'sqlserver';
}
12 changes: 12 additions & 0 deletions tests/Schema/Driver/SQLServer/PrintChangesTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<?php

declare(strict_types=1);

namespace Cycle\Schema\Tests\Driver\SQLServer;

use Cycle\Schema\Tests\Generator\PrintChangesTest as BaseTest;

final class PrintChangesTest extends BaseTest
{
public const DRIVER = 'sqlserver';
}
12 changes: 12 additions & 0 deletions tests/Schema/Driver/SQLite/PrintChangesTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<?php

declare(strict_types=1);

namespace Cycle\Schema\Tests\Driver\SQLite;

use Cycle\Schema\Tests\Generator\PrintChangesTest as BaseTest;

final class PrintChangesTest extends BaseTest
{
public const DRIVER = 'sqlite';
}
5 changes: 4 additions & 1 deletion tests/Schema/Fixtures/Typecaster.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ class Typecaster implements TypecastInterface
{
public function cast(array $values): array
{
// TODO: Implement cast() method.
}

public function setRules(array $rules): array
{
}
}
Loading

0 comments on commit 67da7e7

Please sign in to comment.