diff --git a/src/SQL/Builder/DropSchemaObjectsSQLBuilder.php b/src/SQL/Builder/DropSchemaObjectsSQLBuilder.php index a6fbdda2471..c48d3f9f8e1 100644 --- a/src/SQL/Builder/DropSchemaObjectsSQLBuilder.php +++ b/src/SQL/Builder/DropSchemaObjectsSQLBuilder.php @@ -8,6 +8,7 @@ use Doctrine\DBAL\Schema\Schema; use Doctrine\DBAL\Schema\Sequence; use Doctrine\DBAL\Schema\Table; +use Doctrine\DBAL\Schema\View; use function array_merge; @@ -24,6 +25,7 @@ public function buildSQL(Schema $schema): array $this->buildSequenceStatements($schema->getSequences()), $this->buildTableStatements($schema->getTables()), $this->buildNamespaceStatements($schema->getNamespaces()), + $this->buildViewStatements($schema->getViews()), ); } @@ -72,4 +74,20 @@ private function buildNamespaceStatements(array $namespaces): array return $statements; } + + /** + * @param list $views + * + * @return list + */ + private function buildViewStatements(array $views): array + { + $statements = []; + + foreach ($views as $view) { + $statements[] = $this->platform->getDropViewSQL($view->getQuotedName($this->platform)); + } + + return $statements; + } } diff --git a/src/Schema/AbstractSchemaManager.php b/src/Schema/AbstractSchemaManager.php index 97307979a96..db2a34a8a59 100644 --- a/src/Schema/AbstractSchemaManager.php +++ b/src/Schema/AbstractSchemaManager.php @@ -801,7 +801,9 @@ public function introspectSchema(): Schema $tables = $this->listTables(); - return new Schema($tables, $sequences, $this->createSchemaConfig(), $schemaNames); + $views = $this->listViews(); + + return new Schema($tables, $sequences, $this->createSchemaConfig(), $schemaNames, $views); } /** diff --git a/src/Schema/Exception/ViewAlreadyExists.php b/src/Schema/Exception/ViewAlreadyExists.php new file mode 100644 index 00000000000..badd6c775a4 --- /dev/null +++ b/src/Schema/Exception/ViewAlreadyExists.php @@ -0,0 +1,19 @@ + */ protected array $_sequences = []; + /** @var array */ + protected array $_views = []; + protected SchemaConfig $_schemaConfig; /** * @param array $tables * @param array $sequences * @param array $namespaces + * @param array $views */ public function __construct( array $tables = [], array $sequences = [], ?SchemaConfig $schemaConfig = null, array $namespaces = [], + array $views = [], ) { $schemaConfig ??= new SchemaConfig(); @@ -91,6 +98,10 @@ public function __construct( foreach ($sequences as $sequence) { $this->_addSequence($sequence); } + + foreach ($views as $view) { + $this->_addView($view); + } } protected function _addTable(Table $table): void @@ -134,6 +145,26 @@ protected function _addSequence(Sequence $sequence): void $this->_sequences[$seqName] = $sequence; } + protected function _addView(View $view): void + { + $namespaceName = $view->getNamespaceName(); + $viewName = $this->normalizeName($view); + + if (isset($this->_views[$viewName])) { + throw ViewAlreadyExists::new($viewName); + } + + if ( + $namespaceName !== null + && ! $view->isInDefaultNamespace($this->getName()) + && ! $this->hasNamespace($namespaceName) + ) { + $this->createNamespace($namespaceName); + } + + $this->_views[$viewName] = $view; + } + /** * Returns the namespaces of this schema. * @@ -249,6 +280,29 @@ public function getSequences(): array return array_values($this->_sequences); } + public function hasView(string $name): bool + { + $name = $this->getFullQualifiedAssetName($name); + + return isset($this->_views[$name]); + } + + public function getView(string $name): View + { + $name = $this->getFullQualifiedAssetName($name); + if (! $this->hasView($name)) { + throw ViewDoesNotExist::new($name); + } + + return $this->_views[$name]; + } + + /** @return list */ + public function getViews(): array + { + return array_values($this->_views); + } + /** * Creates a new namespace. * @@ -332,6 +386,26 @@ public function dropSequence(string $name): self return $this; } + /** + * Creates a new view. + */ + public function createView(string $name, string $sql): View + { + $view = new View($name, $sql); + $this->_addView($view); + + return $view; + } + + /** @return $this */ + public function dropView(string $name): self + { + $name = $this->getFullQualifiedAssetName($name); + unset($this->_views[$name]); + + return $this; + } + /** * Returns an array of necessary SQL queries to create the schema on the given platform. * diff --git a/tests/Functional/Schema/SchemaManagerFunctionalTestCase.php b/tests/Functional/Schema/SchemaManagerFunctionalTestCase.php index 7ca35dee0d4..2b4f67be076 100644 --- a/tests/Functional/Schema/SchemaManagerFunctionalTestCase.php +++ b/tests/Functional/Schema/SchemaManagerFunctionalTestCase.php @@ -692,6 +692,25 @@ public function testCreateAndListViews(): void self::assertStringContainsString('view_test_table', $filtered[0]->getSql()); } + public function testIntrospectSchemaWithView(): void + { + $this->createTestTable('view_test_table'); + + $name = 'doctrine_test_view'; + $sql = 'SELECT * FROM view_test_table'; + + $view = new View($name, $sql); + + $this->schemaManager->createView($view); + + $schema = $this->schemaManager->introspectSchema(); + + $filtered = array_values($this->filterElementsByName($schema->getViews(), $name)); + self::assertCount(1, $filtered); + + self::assertStringContainsString('view_test_table', $filtered[0]->getSql()); + } + public function testAutoincrementDetection(): void { if (! $this->connection->getDatabasePlatform()->supportsIdentityColumns()) { diff --git a/tests/Schema/SchemaTest.php b/tests/Schema/SchemaTest.php index 62878b0c8ff..b53c5a60b24 100644 --- a/tests/Schema/SchemaTest.php +++ b/tests/Schema/SchemaTest.php @@ -9,6 +9,7 @@ use Doctrine\DBAL\Schema\SchemaException; use Doctrine\DBAL\Schema\Sequence; use Doctrine\DBAL\Schema\Table; +use Doctrine\DBAL\Schema\View; use Doctrine\DBAL\Types\Types; use PHPUnit\Framework\TestCase; @@ -169,6 +170,73 @@ public function testAddSequenceTwiceThrowsException(): void new Schema([], [$sequence, $sequence]); } + public function testAddViews(): void + { + $view = new View('a_view', 'SELECT 1'); + + $schema = new Schema([], [], null, [], [$view]); + + self::assertTrue($schema->hasView('a_view')); + self::assertSame('a_view', $schema->getView('a_view')->getName()); + + self::assertEquals([$view], $schema->getViews()); + } + + public function testViewAccessCaseInsensitive(): void + { + $view = new View('a_View', 'SELECT 1'); + + $schema = new Schema([], [], null, [], [$view]); + self::assertTrue($schema->hasView('a_view')); + self::assertTrue($schema->hasView('a_View')); + self::assertTrue($schema->hasView('A_VIEW')); + + self::assertEquals($view, $schema->getView('a_view')); + self::assertEquals($view, $schema->getView('a_View')); + self::assertEquals($view, $schema->getView('A_VIEW')); + } + + public function testGetUnknownViewThrowsException(): void + { + $this->expectException(SchemaException::class); + + $schema = new Schema(); + $schema->getView('unknown'); + } + + public function testCreateView(): void + { + $schema = new Schema(); + $view = $schema->createView('a_view', 'SELECT 1'); + + self::assertEquals('a_view', $view->getName()); + self::assertEquals('SELECT 1', $view->getSql()); + + self::assertTrue($schema->hasView('a_view')); + self::assertSame('a_view', $schema->getView('a_view')->getName()); + + self::assertEquals([$view], $schema->getViews()); + } + + public function testDropView(): void + { + $view = new View('a_View', 'SELECT 1'); + + $schema = new Schema([], [], null, [], [$view]); + + $schema->dropView('a_view'); + self::assertFalse($schema->hasView('a_view')); + } + + public function testAddViewTwiceThrowsException(): void + { + $this->expectException(SchemaException::class); + + $view = new View('a_View', 'SELECT 1'); + + new Schema([], [], null, [], [$view, $view]); + } + public function testConfigMaxIdentifierLength(): void { $schemaConfig = new SchemaConfig();