Skip to content

Commit d599d50

Browse files
authored
feat(database): add json data type (tempestphp#709)
1 parent 68d7e54 commit d599d50

File tree

4 files changed

+107
-1
lines changed

4 files changed

+107
-1
lines changed
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Tempest\Database\Exceptions;
6+
7+
use Exception;
8+
9+
final class InvalidDefaultValue extends Exception
10+
{
11+
public function __construct(string $field, string $value)
12+
{
13+
parent::__construct("Default value '{$value}' provided for {$field} is not valid");
14+
}
15+
}

src/Tempest/Database/src/QueryStatements/CreateTableStatement.php

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,20 @@ public function boolean(
166166
return $this;
167167
}
168168

169+
public function json(
170+
string $name,
171+
bool $nullable = false,
172+
?string $default = null
173+
): self {
174+
$this->statements[] = new JsonStatement(
175+
name: $name,
176+
nullable: $nullable,
177+
default: $default,
178+
);
179+
180+
return $this;
181+
}
182+
169183
public function compile(DatabaseDialect $dialect): string
170184
{
171185
$compiled = sprintf(
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Tempest\Database\QueryStatements;
6+
7+
use Tempest\Database\DatabaseDialect;
8+
use Tempest\Database\Exceptions\InvalidDefaultValue;
9+
use Tempest\Database\QueryStatement;
10+
11+
final readonly class JsonStatement implements QueryStatement
12+
{
13+
public function __construct(
14+
private string $name,
15+
private bool $nullable = false,
16+
private ?string $default = null,
17+
) {
18+
}
19+
20+
public function compile(DatabaseDialect $dialect): string
21+
{
22+
if ($this->default && false === json_validate($this->default)) {
23+
throw new InvalidDefaultValue($this->name, $this->default);
24+
}
25+
26+
return match($dialect) {
27+
DatabaseDialect::MYSQL => sprintf(
28+
'`%s` JSON %s',
29+
$this->name,
30+
$this->nullable ? '' : 'NOT NULL',
31+
),
32+
DatabaseDialect::SQLITE => sprintf(
33+
'`%s` TEXT %s %s',
34+
$this->name,
35+
$this->default ? "DEFAULT '{$this->default}'" : '',
36+
$this->nullable ? '' : 'NOT NULL',
37+
),
38+
DatabaseDialect::POSTGRESQL => sprintf(
39+
'`%s` JSONB %s %s',
40+
$this->name,
41+
$this->default ? "DEFAULT (\"{$this->default}\")" : '',
42+
$this->nullable ? '' : 'NOT NULL',
43+
)
44+
};
45+
}
46+
}

tests/Integration/Database/QueryStatements/CreateTableStatementTest.php

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
namespace Tests\Tempest\Integration\Database\QueryStatements;
66

7+
use Tempest\Database\Exceptions\InvalidDefaultValue;
78
use Tempest\Database\Migration;
89
use Tempest\Database\Migrations\CreateMigrationsTable;
910
use Tempest\Database\QueryStatement;
@@ -33,7 +34,8 @@ public function up(): QueryStatement|null
3334
->integer('integer', default: 1)
3435
->date('date', default: '2024-01-01')
3536
->datetime('datetime', default: '2024-01-01 00:00:00')
36-
->boolean('is_active', default: true);
37+
->boolean('is_active', default: true)
38+
->json('json', default: '{"default": "foo"}');
3739
}
3840

3941
public function down(): QueryStatement|null
@@ -50,4 +52,33 @@ public function down(): QueryStatement|null
5052
// Make sure there are no errors
5153
$this->assertTrue(true);
5254
}
55+
56+
public function test_invalid_json_default(): void
57+
{
58+
$migration = new class () implements Migration {
59+
public function getName(): string
60+
{
61+
return '0';
62+
}
63+
64+
public function up(): QueryStatement|null
65+
{
66+
return (new CreateTableStatement('table'))
67+
->json('json', default: '{default: "invalid json"}');
68+
}
69+
70+
public function down(): QueryStatement|null
71+
{
72+
return null;
73+
}
74+
};
75+
76+
$this->expectException(InvalidDefaultValue::class);
77+
$this->expectExceptionMessage("Default value '{default: \"invalid json\"}' provided for json is not valid");
78+
79+
$this->migrate(
80+
CreateMigrationsTable::class,
81+
$migration
82+
);
83+
}
5384
}

0 commit comments

Comments
 (0)