Skip to content

Commit 1d19b19

Browse files
committed
feat: use replace/alter/if not exists for creating views
1 parent d913eab commit 1d19b19

13 files changed

+121
-24
lines changed

src/Grammars/MariaDbGrammar.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ protected function compileViewCreate(View $entity): string
1616
$checkOption = $this->compileCheckOption($entity->checkOption());
1717

1818
return <<<SQL
19-
CREATE VIEW {$entity->name()}{$columns} AS
19+
CREATE OR REPLACE VIEW {$entity->name()}{$columns} AS
2020
{$entity->toString()}
2121
{$checkOption}
2222
SQL;

src/Grammars/MySqlGrammar.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ protected function compileViewCreate(View $entity): string
1616
$checkOption = $this->compileCheckOption($entity->checkOption());
1717

1818
return <<<SQL
19-
CREATE VIEW {$entity->name()}{$columns} AS
19+
CREATE OR REPLACE VIEW {$entity->name()}{$columns} AS
2020
{$entity->toString()}
2121
{$checkOption}
2222
SQL;

src/Grammars/PostgresGrammar.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ protected function compileViewCreate(View $entity): string
1717
$recursive = $entity->isRecursive() ? ' RECURSIVE' : '';
1818

1919
return <<<SQL
20-
CREATE{$recursive} VIEW {$entity->name()}{$columns} AS
20+
CREATE OR REPLACE{$recursive} VIEW {$entity->name()}{$columns} AS
2121
{$entity->toString()}
2222
{$checkOption}
2323
SQL;

src/Grammars/SQLiteGrammar.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ protected function compileViewCreate(View $entity): string
1515
$columns = $this->compileColumnsList($entity->columns());
1616

1717
return <<<SQL
18-
CREATE VIEW {$entity->name()}{$columns} AS
18+
CREATE VIEW IF NOT EXISTS {$entity->name()}{$columns} AS
1919
{$entity->toString()}
2020
SQL;
2121
}

src/Grammars/SqlServerGrammar.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ protected function compileViewCreate(View $entity): string
1616
$columns = $this->compileColumnsList($entity->columns());
1717

1818
return <<<SQL
19-
CREATE VIEW {$entity->name()}{$columns} AS
19+
CREATE OR ALTER VIEW {$entity->name()}{$columns} AS
2020
{$entity->toString()}
2121
{$checkOption}
2222
SQL;

src/SqlEntityManager.php

+29
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,13 @@ class SqlEntityManager
4444
*/
4545
protected array $grammars = [];
4646

47+
/**
48+
* The states of the entities.
49+
*
50+
* @var array<class-string<SqlEntity>, 'created'|'dropped'>
51+
*/
52+
protected array $states = [];
53+
4754
/** @param Collection<array-key, SqlEntity> $entities */
4855
public function __construct(
4956
Collection $entities,
@@ -89,16 +96,25 @@ public function create(SqlEntity|string $entity): void
8996
$entity = $this->get($entity);
9097
}
9198

99+
if (($this->states[$entity::class] ?? null) === 'created') {
100+
return;
101+
}
102+
92103
$connection = $this->connection($entity->connectionName());
93104

94105
if (! $entity->creating($connection)) {
95106
return;
96107
}
97108

109+
foreach ($entity->dependencies() as $dependency) {
110+
$this->create($dependency);
111+
}
112+
98113
$grammar = $this->grammar($connection);
99114

100115
$connection->statement($grammar->compileCreate($entity));
101116
$entity->created($connection);
117+
$this->states[$entity::class] = 'created';
102118
}
103119

104120
/**
@@ -113,6 +129,10 @@ public function drop(SqlEntity|string $entity): void
113129
$entity = $this->get($entity);
114130
}
115131

132+
if (($this->states[$entity::class] ?? null) === 'dropped') {
133+
return;
134+
}
135+
116136
$connection = $this->connection($entity->connectionName());
117137

118138
if (! $entity->dropping($connection)) {
@@ -123,6 +143,7 @@ public function drop(SqlEntity|string $entity): void
123143

124144
$connection->statement($grammar->compileDrop($entity));
125145
$entity->dropped($connection);
146+
$this->states[$entity::class] = 'dropped';
126147
}
127148

128149
/**
@@ -201,6 +222,14 @@ public function withoutEntities(
201222
}
202223
}
203224

225+
/** Flush the entity manager instance. */
226+
public function flush(): void
227+
{
228+
$this->connections = [];
229+
$this->grammars = [];
230+
$this->states = [];
231+
}
232+
204233
/**
205234
* Filter entities by connection.
206235
*

tests/Feature/SqlEntityManagerTest.php

+49-4
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
use Illuminate\Database\Grammar;
1111
use Illuminate\Support\ItemNotFoundException;
1212
use Workbench\Database\Entities\views\FooConnectionUserView;
13+
use Workbench\Database\Entities\views\NewUserView;
1314
use Workbench\Database\Entities\views\UserView;
1415

1516
dataset('drivers', [
@@ -21,11 +22,11 @@
2122
]);
2223

2324
dataset('typesAndConnections', [
24-
'default args' => ['types' => null, 'connections' => null, 'times' => 2],
25+
'default args' => ['types' => null, 'connections' => null, 'times' => 3],
2526
'single specific type' => ['types' => UserView::class, 'connections' => null, 'times' => 1],
26-
'single connection' => ['types' => null, 'connections' => 'default', 'times' => 1],
27-
'multiple connections' => ['types' => null, 'connections' => ['default', 'foo'], 'times' => 2],
28-
'single abstract type' => ['types' => View::class, 'connections' => null, 'times' => 2],
27+
'single connection' => ['types' => null, 'connections' => 'default', 'times' => 2],
28+
'multiple connections' => ['types' => null, 'connections' => ['default', 'foo'], 'times' => 3],
29+
'single abstract type' => ['types' => View::class, 'connections' => null, 'times' => 3],
2930
'multiple types' => ['types' => [UserView::class, FooConnectionUserView::class], 'connections' => null, 'times' => 2],
3031
]);
3132

@@ -86,6 +87,27 @@
8687
$entity->shouldCreate = false;
8788
test()->manager->create($entity);
8889
});
90+
91+
it('skips already created entities', function () {
92+
test()->connection
93+
->shouldReceive('getDriverName')->once()->andReturn('sqlite')
94+
->shouldReceive('statement')
95+
->once()
96+
->withArgs(fn ($sql) => str_contains($sql, 'CREATE'));
97+
98+
test()->manager->create(UserView::class);
99+
test()->manager->create(UserView::class);
100+
});
101+
102+
it('creates an entity\'s dependencies', function () {
103+
test()->connection
104+
->shouldReceive('getDriverName')->times(2)->andReturn('sqlite')
105+
->shouldReceive('statement')
106+
->times(2)
107+
->withArgs(fn ($sql) => str_contains($sql, 'CREATE'));
108+
109+
test()->manager->create(NewUserView::class);
110+
});
89111
});
90112

91113
describe('drop', function () {
@@ -112,6 +134,17 @@
112134
$entity->shouldDrop = false;
113135
test()->manager->drop($entity);
114136
});
137+
138+
it('skips already dropped entities', function () {
139+
test()->connection
140+
->shouldReceive('getDriverName')->once()->andReturn('sqlite')
141+
->shouldReceive('statement')
142+
->once()
143+
->withArgs(fn ($sql) => str_contains($sql, 'DROP'));
144+
145+
test()->manager->drop(UserView::class);
146+
test()->manager->drop(UserView::class);
147+
});
115148
});
116149

117150
it('creates entities by type and connection', function (array|string|null $types, array|string|null $connections, int $times) {
@@ -187,3 +220,15 @@
187220

188221
test()->manager->create(new UserView());
189222
})->throws(InvalidArgumentException::class, 'Unsupported driver [unknown].');
223+
224+
it('flushes the instance', function () {
225+
test()->connection
226+
->shouldReceive('getDriverName')->times(2)->andReturn('sqlite')
227+
->shouldReceive('statement')
228+
->times(2)
229+
->withArgs(fn ($sql) => str_contains($sql, 'CREATE'));
230+
231+
test()->manager->create(UserView::class);
232+
test()->manager->flush();
233+
test()->manager->create(UserView::class);
234+
});

tests/Unit/Grammars/MariaDbGrammarTest.php

+3-3
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
$sql = test()->grammar->compileCreate(test()->entity);
1919

2020
expect($sql)->toBe(<<<'SQL'
21-
CREATE VIEW user_view AS
21+
CREATE OR REPLACE VIEW user_view AS
2222
SELECT id, name FROM users
2323

2424
SQL);
@@ -30,7 +30,7 @@
3030
$sql = test()->grammar->compileCreate(test()->entity);
3131

3232
expect($sql)->toBe(<<<SQL
33-
CREATE VIEW user_view{$expected} AS
33+
CREATE OR REPLACE VIEW user_view{$expected} AS
3434
SELECT id, name FROM users
3535
3636
SQL);
@@ -45,7 +45,7 @@
4545
$sql = test()->grammar->compileCreate(test()->entity);
4646

4747
expect($sql)->toBe(<<<SQL
48-
CREATE VIEW user_view AS
48+
CREATE OR REPLACE VIEW user_view AS
4949
SELECT id, name FROM users
5050
{$expected}
5151
SQL);

tests/Unit/Grammars/MySqlGrammarTest.php

+3-3
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
$sql = test()->grammar->compileCreate(test()->entity);
1919

2020
expect($sql)->toBe(<<<'SQL'
21-
CREATE VIEW user_view AS
21+
CREATE OR REPLACE VIEW user_view AS
2222
SELECT id, name FROM users
2323

2424
SQL);
@@ -30,7 +30,7 @@
3030
$sql = test()->grammar->compileCreate(test()->entity);
3131

3232
expect($sql)->toBe(<<<SQL
33-
CREATE VIEW user_view{$expected} AS
33+
CREATE OR REPLACE VIEW user_view{$expected} AS
3434
SELECT id, name FROM users
3535
3636
SQL);
@@ -45,7 +45,7 @@
4545
$sql = test()->grammar->compileCreate(test()->entity);
4646

4747
expect($sql)->toBe(<<<SQL
48-
CREATE VIEW user_view AS
48+
CREATE OR REPLACE VIEW user_view AS
4949
SELECT id, name FROM users
5050
{$expected}
5151
SQL);

tests/Unit/Grammars/PostgresGrammarTest.php

+4-4
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
$sql = test()->grammar->compileCreate(test()->entity);
1919

2020
expect($sql)->toBe(<<<'SQL'
21-
CREATE VIEW user_view AS
21+
CREATE OR REPLACE VIEW user_view AS
2222
SELECT id, name FROM users
2323

2424
SQL);
@@ -30,7 +30,7 @@
3030
$sql = test()->grammar->compileCreate(test()->entity);
3131

3232
expect($sql)->toBe(<<<'SQL'
33-
CREATE RECURSIVE VIEW user_view AS
33+
CREATE OR REPLACE RECURSIVE VIEW user_view AS
3434
SELECT id, name FROM users
3535

3636
SQL);
@@ -42,7 +42,7 @@
4242
$sql = test()->grammar->compileCreate(test()->entity);
4343

4444
expect($sql)->toBe(<<<SQL
45-
CREATE VIEW user_view{$expected} AS
45+
CREATE OR REPLACE VIEW user_view{$expected} AS
4646
SELECT id, name FROM users
4747
4848
SQL);
@@ -57,7 +57,7 @@
5757
$sql = test()->grammar->compileCreate(test()->entity);
5858

5959
expect($sql)->toBe(<<<SQL
60-
CREATE VIEW user_view AS
60+
CREATE OR REPLACE VIEW user_view AS
6161
SELECT id, name FROM users
6262
{$expected}
6363
SQL);

tests/Unit/Grammars/SQLiteGrammarTest.php

+2-2
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
$sql = test()->grammar->compileCreate(test()->entity);
1919

2020
expect($sql)->toBe(<<<'SQL'
21-
CREATE VIEW user_view AS
21+
CREATE VIEW IF NOT EXISTS user_view AS
2222
SELECT id, name FROM users
2323
SQL);
2424
});
@@ -29,7 +29,7 @@
2929
$sql = test()->grammar->compileCreate(test()->entity);
3030

3131
expect($sql)->toBe(<<<SQL
32-
CREATE VIEW user_view{$expected} AS
32+
CREATE VIEW IF NOT EXISTS user_view{$expected} AS
3333
SELECT id, name FROM users
3434
SQL);
3535
})->with([

tests/Unit/Grammars/SqlServerGrammarTest.php

+3-3
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
$sql = test()->grammar->compileCreate(test()->entity);
1919

2020
expect($sql)->toBe(<<<'SQL'
21-
CREATE VIEW user_view AS
21+
CREATE OR ALTER VIEW user_view AS
2222
SELECT id, name FROM users
2323

2424
SQL);
@@ -30,7 +30,7 @@
3030
$sql = test()->grammar->compileCreate(test()->entity);
3131

3232
expect($sql)->toBe(<<<SQL
33-
CREATE VIEW user_view{$expected} AS
33+
CREATE OR ALTER VIEW user_view{$expected} AS
3434
SELECT id, name FROM users
3535
3636
SQL);
@@ -45,7 +45,7 @@
4545
$sql = test()->grammar->compileCreate(test()->entity);
4646

4747
expect($sql)->toBe(<<<SQL
48-
CREATE VIEW user_view AS
48+
CREATE OR ALTER VIEW user_view AS
4949
SELECT id, name FROM users
5050
{$expected}
5151
SQL);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Workbench\Database\Entities\views;
6+
7+
use CalebDW\SqlEntities\View;
8+
use Override;
9+
10+
class NewUserView extends View
11+
{
12+
/** @inheritDoc */
13+
protected array $dependencies = [UserView::class];
14+
15+
#[Override]
16+
public function definition(): string
17+
{
18+
return <<<'SQL'
19+
SELECT id, name FROM users_view
20+
WHERE created_at > NOW() - INTERVAL '1 day'
21+
SQL;
22+
}
23+
}

0 commit comments

Comments
 (0)