Skip to content

Commit 580a7a0

Browse files
morloderextpetry
andauthored
Add case when syntax (tpetry#19)
Co-authored-by: tpetry <[email protected]>
1 parent 48eaa8a commit 580a7a0

File tree

4 files changed

+146
-0
lines changed

4 files changed

+146
-0
lines changed

src/Language/CaseGroup.php

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Tpetry\QueryExpressions\Language;
6+
7+
use Illuminate\Contracts\Database\Query\Expression;
8+
use Illuminate\Database\Grammar;
9+
use Tpetry\QueryExpressions\Concerns\IdentifiesDriver;
10+
use Tpetry\QueryExpressions\Concerns\StringizeExpression;
11+
12+
class CaseGroup implements Expression
13+
{
14+
use IdentifiesDriver;
15+
use StringizeExpression;
16+
17+
/**
18+
* @param non-empty-array<int, CaseRule> $when
19+
*/
20+
public function __construct(
21+
private readonly array $when,
22+
private readonly string|Expression|null $else = null,
23+
) {
24+
}
25+
26+
public function getValue(Grammar $grammar): string
27+
{
28+
$conditions = array_map(
29+
callback: fn ($expression) => $this->stringize($grammar, $expression),
30+
array: $this->when,
31+
);
32+
$conditions = implode(' ', $conditions);
33+
34+
return match ($this->else) {
35+
null => "(case {$conditions} end)",
36+
default => "(case {$conditions} else {$this->stringize($grammar, $this->else)} end)",
37+
};
38+
}
39+
}

src/Language/CaseRule.php

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Tpetry\QueryExpressions\Language;
6+
7+
use Illuminate\Contracts\Database\Query\ConditionExpression;
8+
use Illuminate\Contracts\Database\Query\Expression;
9+
use Illuminate\Database\Grammar;
10+
use Tpetry\QueryExpressions\Concerns\StringizeExpression;
11+
12+
class CaseRule implements Expression
13+
{
14+
use StringizeExpression;
15+
16+
public function __construct(
17+
private readonly string|Expression $result,
18+
private readonly ConditionExpression $condition,
19+
) {
20+
}
21+
22+
public function getValue(Grammar $grammar): string
23+
{
24+
$condition = $this->stringize($grammar, $this->condition);
25+
$result = $this->stringize($grammar, $this->result);
26+
27+
return "when {$condition} then {$result}";
28+
}
29+
}

tests/ConditionExpression.php

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Tpetry\QueryExpressions\Tests;
6+
7+
use Illuminate\Contracts\Database\Query\ConditionExpression as ConditionExpressionContract;
8+
use Illuminate\Database\Query\Expression;
9+
10+
class ConditionExpression extends Expression implements ConditionExpressionContract
11+
{
12+
}

tests/Language/CaseTest.php

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
use Illuminate\Database\Query\Expression;
6+
use Illuminate\Database\Schema\Blueprint;
7+
use Tpetry\QueryExpressions\Language\CaseGroup;
8+
use Tpetry\QueryExpressions\Language\CaseRule;
9+
use Tpetry\QueryExpressions\Tests\ConditionExpression;
10+
11+
it('can create create a case-expression with a single branch')
12+
->expect(
13+
new CaseGroup([
14+
new CaseRule(new Expression(2), new ConditionExpression('1 = 1')),
15+
])
16+
)
17+
->toBeExecutable()
18+
->toBeMysql('(case when 1 = 1 then 2 end)')
19+
->toBePgsql('(case when 1 = 1 then 2 end)')
20+
->toBeSqlite('(case when 1 = 1 then 2 end)')
21+
->toBeSqlsrv('(case when 1 = 1 then 2 end)');
22+
23+
it('can create create a case-expression with multiple branches')
24+
->expect(new CaseGroup([
25+
new CaseRule(new Expression(2), new ConditionExpression('1 = 1')),
26+
new CaseRule('val', new ConditionExpression('2 = 2')),
27+
]))
28+
->toBeExecutable(function (Blueprint $table) {
29+
$table->integer('val');
30+
})
31+
->toBeMysql('(case when 1 = 1 then 2 when 2 = 2 then `val` end)')
32+
->toBePgsql('(case when 1 = 1 then 2 when 2 = 2 then "val" end)')
33+
->toBeSqlite('(case when 1 = 1 then 2 when 2 = 2 then "val" end)')
34+
->toBeSqlsrv('(case when 1 = 1 then 2 when 2 = 2 then [val] end)');
35+
36+
it('can create create a case-expression with multiple branches and expression default')
37+
->expect(new CaseGroup(
38+
[
39+
new CaseRule(new Expression(2), new ConditionExpression('1 = 1')),
40+
new CaseRule('val', new ConditionExpression('2 = 2')),
41+
],
42+
new Expression('4'),
43+
))
44+
->toBeExecutable(function (Blueprint $table) {
45+
$table->integer('val');
46+
})
47+
->toBeMysql('(case when 1 = 1 then 2 when 2 = 2 then `val` else 4 end)')
48+
->toBePgsql('(case when 1 = 1 then 2 when 2 = 2 then "val" else 4 end)')
49+
->toBeSqlite('(case when 1 = 1 then 2 when 2 = 2 then "val" else 4 end)')
50+
->toBeSqlsrv('(case when 1 = 1 then 2 when 2 = 2 then [val] else 4 end)');
51+
52+
it('can create create a case-expression with multiple branches and column default')
53+
->expect(new CaseGroup(
54+
[
55+
new CaseRule(new Expression(2), new ConditionExpression('1 = 1')),
56+
new CaseRule('val', new ConditionExpression('2 = 2')),
57+
],
58+
'val',
59+
))
60+
->toBeExecutable(function (Blueprint $table) {
61+
$table->integer('val');
62+
})
63+
->toBeMysql('(case when 1 = 1 then 2 when 2 = 2 then `val` else `val` end)')
64+
->toBePgsql('(case when 1 = 1 then 2 when 2 = 2 then "val" else "val" end)')
65+
->toBeSqlite('(case when 1 = 1 then 2 when 2 = 2 then "val" else "val" end)')
66+
->toBeSqlsrv('(case when 1 = 1 then 2 when 2 = 2 then [val] else [val] end)');

0 commit comments

Comments
 (0)