diff --git a/README.md b/README.md index 9b7d8ae..f974a6e 100644 --- a/README.md +++ b/README.md @@ -16,6 +16,7 @@ php composer.phar require umbrellio/laravel-pg-extensions - [Extended `Schema::create()`](#extended-table-creation) - [Extended `Schema` with GIST/GIN indexes](#create-gist/gin-indexes) + - [Extended `Schema` for views](#create-views) - [Working with unique indexes](#extended-unique-indexes-creation) - [Working with partitions](#partitions) - [Check existing index before manipulation](#check-existing-index) @@ -39,6 +40,22 @@ Schema::create('table', function (Blueprint $table) { }); ``` +### Create views + +Example: +```php +// Facade methods: +Schema::createView('active_users', "SELECT * FROM users WHERE active = 1"); +Schema::dropView('active_users') + +// Schema methods: +Schema::create('users', function (Blueprint $table) { + $table + ->createView('active_users', , "SELECT * FROM users WHERE active = 1") + ->materialize(); +}); +``` + ### Extended unique indexes creation Example: diff --git a/src/.meta.php b/src/.meta.php index 2441843..30942b0 100644 --- a/src/.meta.php +++ b/src/.meta.php @@ -5,6 +5,7 @@ use Illuminate\Support\Fluent; use Umbrellio\Postgres\Schema\Definitions\AttachPartitionDefinition; use Umbrellio\Postgres\Schema\Definitions\LikeDefinition; + use Umbrellio\Postgres\Schema\Definitions\ViewDefinition; use Umbrellio\Postgres\Schema\Definitions\UniqueDefinition; /** @@ -13,6 +14,8 @@ * @method LikeDefinition like(string $table) * @method Fluent ifNotExists() * @method UniqueDefinition uniquePartial($columns, ?string $index = null, ?string $algorithm = null) + * @method ViewDefinition createView(string $view, string $select, bool $materialize = false) + * @method Fluent dropView(string $view) * @method Fluent gin($columns, ?string $name = null) * @method Fluent gist($columns, ?string $name = null) * @method ColumnDefinition numeric(string $column, ?int $precision = null, ?int $scale = null): diff --git a/src/Schema/Blueprint.php b/src/Schema/Blueprint.php index ea057da..774a84a 100644 --- a/src/Schema/Blueprint.php +++ b/src/Schema/Blueprint.php @@ -12,6 +12,7 @@ use Umbrellio\Postgres\Schema\Definitions\AttachPartitionDefinition; use Umbrellio\Postgres\Schema\Definitions\LikeDefinition; use Umbrellio\Postgres\Schema\Definitions\UniqueDefinition; +use Umbrellio\Postgres\Schema\Definitions\ViewDefinition; class Blueprint extends BaseBlueprint { @@ -87,6 +88,19 @@ public function hasIndex($index, bool $unique = false): bool return array_key_exists($index, $this->getSchemaManager()->listTableIndexes($this->getTable())); } + /** + * @return ViewDefinition|Fluent + */ + public function createView(string $view, string $select, bool $materialize = false): Fluent + { + return $this->addCommand('createView', compact('view', 'select', 'materialize')); + } + + public function dropView(string $view): Fluent + { + return $this->addCommand('dropView', compact('view')); + } + /** * Almost like 'decimal' type, but can be with variable precision (by default) */ diff --git a/src/Schema/Builder.php b/src/Schema/Builder.php index 03c1aeb..c3dd100 100644 --- a/src/Schema/Builder.php +++ b/src/Schema/Builder.php @@ -12,6 +12,37 @@ class Builder extends BasePostgresBuilder { use Macroable; + public function createView(string $view, string $select, $materialize = false): void + { + $blueprint = $this->createBlueprint($view); + $blueprint->createView($view, $select, $materialize); + $this->build($blueprint); + } + + public function dropView(string $view): void + { + $blueprint = $this->createBlueprint($view); + $blueprint->dropView($view); + $this->build($blueprint); + } + + public function hasView(string $view): bool + { + return count($this->connection->selectFromWriteConnection($this->grammar->compileViewExists(), [ + $this->connection->getConfig()['schema'], + $this->connection->getTablePrefix() . $view, + ])) > 0; + } + + public function getViewDefinition($view): string + { + $results = $this->connection->selectFromWriteConnection($this->grammar->compileViewDefinition(), [ + $this->connection->getConfig()['schema'], + $this->connection->getTablePrefix() . $view, + ]); + return count($results) > 0 ? $results[0]->view_definition : ''; + } + /** * @param string $table * @param Closure|null $callback diff --git a/src/Schema/Definitions/ViewDefinition.php b/src/Schema/Definitions/ViewDefinition.php new file mode 100644 index 0000000..c3d328b --- /dev/null +++ b/src/Schema/Definitions/ViewDefinition.php @@ -0,0 +1,14 @@ +get('materialize') ? 'materialized' : ''; + return implode(' ', array_filter([ + 'create', $materialize, 'view', + $this->wrapTable($command->get('view')), + 'as', $command->get('select'), + ])); + } + + public function compileDropView(Blueprint $blueprint, Fluent $command): string + { + return 'drop view ' . $this->wrapTable($command->get('view')); + } + + public function compileViewExists(): string + { + return 'select * from information_schema.views where table_schema = ? and table_name = ?'; + } + + public function compileViewDefinition(): string + { + return 'select view_definition from information_schema.views where table_schema = ? and table_name = ?'; + } + public function compileUniquePartial(Blueprint $blueprint, UniquePartialBuilder $command): string { $constraints = $command->get('constraints'); diff --git a/tests/Functional/ViewTest.php b/tests/Functional/ViewTest.php new file mode 100644 index 0000000..755255e --- /dev/null +++ b/tests/Functional/ViewTest.php @@ -0,0 +1,63 @@ +increments('id'); + $table->string('name'); + }); + } + + protected function tearDown(): void + { + Schema::dropIfExists('test_table'); + parent::tearDown(); + } + + /** @test */ + public function createFacadeView(): void + { + Schema::createView('test_view', 'select * from test_table where name is not null'); + + $this->assertSame( + strtolower( + 'select test_table.id, test_table.name from test_table where (test_table.name is not null);' + ), + trim(strtolower(str_replace("\n", ' ', Schema::getViewDefinition('test_view')))) + ); + + Schema::dropView('test_view'); + $this->assertFalse(Schema::hasView('test_view')); + } + + /** @test */ + public function createBlueprintView(): void + { + Schema::create('users', function (Blueprint $table) { + $table->increments('id'); + $table->string('name'); + $table->createView('test_view', 'select * from users where name is not null'); + }); + + $this->assertSame( + strtolower('select users.id, users.name from users where (users.name is not null);'), + trim(strtolower(str_replace("\n", ' ', Schema::getViewDefinition('test_view')))) + ); + + Schema::table('users', function (Blueprint $table) { + $table->dropView('test_view'); + }); + + $this->assertFalse(Schema::hasView('test_view')); + } +}