diff --git a/lib/Doctrine/Migrations/Generator/DiffGenerator.php b/lib/Doctrine/Migrations/Generator/DiffGenerator.php index bd5c74693..777755700 100644 --- a/lib/Doctrine/Migrations/Generator/DiffGenerator.php +++ b/lib/Doctrine/Migrations/Generator/DiffGenerator.php @@ -12,6 +12,7 @@ use Doctrine\Migrations\Generator\Exception\NoChangesDetected; use Doctrine\Migrations\Provider\SchemaProvider; +use function method_exists; use function preg_match; use function strpos; use function substr; @@ -95,15 +96,27 @@ static function ($assetName) use ($filterExpression) { $toSchema = $this->createToSchema(); + if (method_exists($this->schemaManager, 'createComparator')) { + $comparator = $this->schemaManager->createComparator(); + } + + $upSql = isset($comparator) ? + $comparator->compareSchemas($fromSchema, $toSchema)->toSql($this->platform) : + $fromSchema->getMigrateToSql($toSchema, $this->platform); + $up = $this->migrationSqlGenerator->generate( - $fromSchema->getMigrateToSql($toSchema, $this->platform), + $upSql, $formatted, $lineLength, $checkDbPlatform ); + $downSql = isset($comparator) ? + $comparator->compareSchemas($toSchema, $fromSchema)->toSql($this->platform) : + $fromSchema->getMigrateFromSql($toSchema, $this->platform); + $down = $this->migrationSqlGenerator->generate( - $fromSchema->getMigrateFromSql($toSchema, $this->platform), + $downSql, $formatted, $lineLength, $checkDbPlatform diff --git a/lib/Doctrine/Migrations/Provider/DBALSchemaDiffProvider.php b/lib/Doctrine/Migrations/Provider/DBALSchemaDiffProvider.php index ccccb17f3..ba9db6778 100644 --- a/lib/Doctrine/Migrations/Provider/DBALSchemaDiffProvider.php +++ b/lib/Doctrine/Migrations/Provider/DBALSchemaDiffProvider.php @@ -8,6 +8,8 @@ use Doctrine\DBAL\Schema\AbstractSchemaManager; use Doctrine\DBAL\Schema\Schema; +use function method_exists; + /** * The SchemaDiffProvider class is responsible for providing a Doctrine\DBAL\Schema\Schema instance that * represents the current state of your database. A clone of this Schema instance is passed to each of your migrations @@ -48,6 +50,13 @@ public function createToSchema(Schema $fromSchema): Schema /** @return string[] */ public function getSqlDiffToMigrate(Schema $fromSchema, Schema $toSchema): array { - return $fromSchema->getMigrateToSql($toSchema, $this->platform); + if (! method_exists($this->schemaManager, 'createComparator')) { + return $fromSchema->getMigrateToSql($toSchema, $this->platform); + } + + return $this->schemaManager->createComparator()->compareSchemas( + $fromSchema, + $toSchema + )->toSql($this->platform); } } diff --git a/phpstan-dbal-3.neon.dist b/phpstan-dbal-3.neon.dist index f94eca0cf..bfa27b10e 100644 --- a/phpstan-dbal-3.neon.dist +++ b/phpstan-dbal-3.neon.dist @@ -23,13 +23,27 @@ parameters: - tests/Doctrine/Migrations/Tests/Tools/Console/legacy-config-dbal/cli-config.php - tests/Doctrine/Migrations/Tests/Tools/Console/legacy-config-wrong/cli-config.php - # We need to implement platform-aware comparison + # Compatibility with DBAL 2 - message: '~Call to deprecated method getMigrate(From|ToSql)~' paths: - lib/Doctrine/Migrations/Generator/DiffGenerator.php - lib/Doctrine/Migrations/Provider/DBALSchemaDiffProvider.php + - + message: "~Call to function method_exists.*'createComparator' will always evaluate to true.~" + paths: + - lib/Doctrine/Migrations/Generator/DiffGenerator.php + - lib/Doctrine/Migrations/Provider/DBALSchemaDiffProvider.php + - tests/Doctrine/Migrations/Tests/Generator/DiffGeneratorTest.php + + # That method will no longer be static in the future + - + message: '~Dynamic call to static method Doctrine\\DBAL\\Schema\\Comparator\:\:compareSchemas\(\)\.~' + paths: + - lib/Doctrine/Migrations/Generator/DiffGenerator.php + - lib/Doctrine/Migrations/Provider/DBALSchemaDiffProvider.php + # Switch to Logging\Connection after dropping compatibility with DBAL 2 - message: '~deprecated.*DebugStack~' diff --git a/tests/Doctrine/Migrations/Tests/Generator/DiffGeneratorTest.php b/tests/Doctrine/Migrations/Tests/Generator/DiffGeneratorTest.php index 8fe37ca00..a614e1047 100644 --- a/tests/Doctrine/Migrations/Tests/Generator/DiffGeneratorTest.php +++ b/tests/Doctrine/Migrations/Tests/Generator/DiffGeneratorTest.php @@ -7,7 +7,9 @@ use Doctrine\DBAL\Configuration as DBALConfiguration; use Doctrine\DBAL\Platforms\AbstractPlatform; use Doctrine\DBAL\Schema\AbstractSchemaManager; +use Doctrine\DBAL\Schema\Comparator; use Doctrine\DBAL\Schema\Schema; +use Doctrine\DBAL\Schema\SchemaDiff; use Doctrine\DBAL\Schema\Table; use Doctrine\Migrations\Generator\DiffGenerator; use Doctrine\Migrations\Generator\Generator; @@ -16,6 +18,8 @@ use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; +use function method_exists; + class DiffGeneratorTest extends TestCase { /** @var DBALConfiguration|MockObject */ @@ -92,15 +96,42 @@ static function ($name): bool { ->method('dropTable') ->will(self::onConsecutiveCalls('schema.table_name2', 'schema.table_name3')); - $fromSchema->expects(self::once()) - ->method('getMigrateToSql') - ->with($toSchema, $this->platform) - ->willReturn(['UPDATE table SET value = 2']); + if (method_exists($this->schemaManager, 'createComparator')) { + $schemaDiff = $this->createStub(SchemaDiff::class); + $schemaDiff->method('toSql')->willReturn(self::onConsecutiveCalls( + ['UPDATE table SET value = 2'], + ['UPDATE table SET value = 1'] + )); + + // regular mocks cannot be used here, because the method is static + $comparator = new class extends Comparator { + /** @var SchemaDiff */ + public static $schemaDiff; + + public static function compareSchemas( + Schema $fromSchema, + Schema $toSchema + ): SchemaDiff { + return self::$schemaDiff; + } + }; + + $comparator::$schemaDiff = $schemaDiff; - $fromSchema->expects(self::once()) - ->method('getMigrateFromSql') - ->with($toSchema, $this->platform) - ->willReturn(['UPDATE table SET value = 1']); + $this->schemaManager->expects(self::once()) + ->method('createComparator') + ->willReturn($comparator); + } else { + $fromSchema->expects(self::once()) + ->method('getMigrateToSql') + ->with($toSchema, $this->platform) + ->willReturn(['UPDATE table SET value = 2']); + + $fromSchema->expects(self::once()) + ->method('getMigrateFromSql') + ->with($toSchema, $this->platform) + ->willReturn(['UPDATE table SET value = 1']); + } $this->migrationSqlGenerator->expects(self::exactly(2)) ->method('generate') @@ -115,7 +146,12 @@ static function ($name): bool { ->with('1234', 'test1', 'test2') ->willReturn('path'); - self::assertSame('path', $this->migrationDiffGenerator->generate('1234', '/table_name1/', true, 80)); + self::assertSame('path', $this->migrationDiffGenerator->generate( + '1234', + '/table_name1/', + true, + 80 + )); } public function testGenerateFromEmptySchema(): void @@ -147,15 +183,42 @@ public function testGenerateFromEmptySchema(): void $toSchema->expects(self::never()) ->method('dropTable'); - $emptySchema->expects(self::once()) - ->method('getMigrateToSql') - ->with($toSchema, $this->platform) - ->willReturn(['CREATE TABLE table_name']); - - $emptySchema->expects(self::once()) - ->method('getMigrateFromSql') - ->with($toSchema, $this->platform) - ->willReturn(['DROP TABLE table_name']); + if (method_exists($this->schemaManager, 'createComparator')) { + $schemaDiff = $this->createStub(SchemaDiff::class); + $schemaDiff->method('toSql')->willReturn(self::onConsecutiveCalls( + ['CREATE TABLE table_name'], + ['DROP TABLE table_name'] + )); + + // regular mocks cannot be used here, because the method is static + $comparator = new class extends Comparator { + /** @var SchemaDiff */ + public static $schemaDiff; + + public static function compareSchemas( + Schema $fromSchema, + Schema $toSchema + ): SchemaDiff { + return self::$schemaDiff; + } + }; + + $comparator::$schemaDiff = $schemaDiff; + + $this->schemaManager->expects(self::once()) + ->method('createComparator') + ->willReturn($comparator); + } else { + $emptySchema->expects(self::once()) + ->method('getMigrateToSql') + ->with($toSchema, $this->platform) + ->willReturn(['CREATE TABLE table_name']); + + $emptySchema->expects(self::once()) + ->method('getMigrateFromSql') + ->with($toSchema, $this->platform) + ->willReturn(['DROP TABLE table_name']); + } $this->migrationSqlGenerator->expects(self::exactly(2)) ->method('generate')