Skip to content

Commit

Permalink
Add case when syntax (#19)
Browse files Browse the repository at this point in the history
Co-authored-by: tpetry <[email protected]>
  • Loading branch information
morloderex and tpetry authored Nov 30, 2023
1 parent 48eaa8a commit 580a7a0
Show file tree
Hide file tree
Showing 4 changed files with 146 additions and 0 deletions.
39 changes: 39 additions & 0 deletions src/Language/CaseGroup.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
<?php

declare(strict_types=1);

namespace Tpetry\QueryExpressions\Language;

use Illuminate\Contracts\Database\Query\Expression;
use Illuminate\Database\Grammar;
use Tpetry\QueryExpressions\Concerns\IdentifiesDriver;
use Tpetry\QueryExpressions\Concerns\StringizeExpression;

class CaseGroup implements Expression
{
use IdentifiesDriver;
use StringizeExpression;

/**
* @param non-empty-array<int, CaseRule> $when
*/
public function __construct(
private readonly array $when,
private readonly string|Expression|null $else = null,
) {
}

public function getValue(Grammar $grammar): string
{
$conditions = array_map(
callback: fn ($expression) => $this->stringize($grammar, $expression),
array: $this->when,
);
$conditions = implode(' ', $conditions);

return match ($this->else) {
null => "(case {$conditions} end)",
default => "(case {$conditions} else {$this->stringize($grammar, $this->else)} end)",
};
}
}
29 changes: 29 additions & 0 deletions src/Language/CaseRule.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
<?php

declare(strict_types=1);

namespace Tpetry\QueryExpressions\Language;

use Illuminate\Contracts\Database\Query\ConditionExpression;
use Illuminate\Contracts\Database\Query\Expression;
use Illuminate\Database\Grammar;
use Tpetry\QueryExpressions\Concerns\StringizeExpression;

class CaseRule implements Expression
{
use StringizeExpression;

public function __construct(
private readonly string|Expression $result,
private readonly ConditionExpression $condition,
) {
}

public function getValue(Grammar $grammar): string
{
$condition = $this->stringize($grammar, $this->condition);
$result = $this->stringize($grammar, $this->result);

return "when {$condition} then {$result}";
}
}
12 changes: 12 additions & 0 deletions tests/ConditionExpression.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<?php

declare(strict_types=1);

namespace Tpetry\QueryExpressions\Tests;

use Illuminate\Contracts\Database\Query\ConditionExpression as ConditionExpressionContract;
use Illuminate\Database\Query\Expression;

class ConditionExpression extends Expression implements ConditionExpressionContract
{
}
66 changes: 66 additions & 0 deletions tests/Language/CaseTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
<?php

declare(strict_types=1);

use Illuminate\Database\Query\Expression;
use Illuminate\Database\Schema\Blueprint;
use Tpetry\QueryExpressions\Language\CaseGroup;
use Tpetry\QueryExpressions\Language\CaseRule;
use Tpetry\QueryExpressions\Tests\ConditionExpression;

it('can create create a case-expression with a single branch')
->expect(
new CaseGroup([
new CaseRule(new Expression(2), new ConditionExpression('1 = 1')),
])
)
->toBeExecutable()
->toBeMysql('(case when 1 = 1 then 2 end)')
->toBePgsql('(case when 1 = 1 then 2 end)')
->toBeSqlite('(case when 1 = 1 then 2 end)')
->toBeSqlsrv('(case when 1 = 1 then 2 end)');

it('can create create a case-expression with multiple branches')
->expect(new CaseGroup([
new CaseRule(new Expression(2), new ConditionExpression('1 = 1')),
new CaseRule('val', new ConditionExpression('2 = 2')),
]))
->toBeExecutable(function (Blueprint $table) {
$table->integer('val');
})
->toBeMysql('(case when 1 = 1 then 2 when 2 = 2 then `val` end)')
->toBePgsql('(case when 1 = 1 then 2 when 2 = 2 then "val" end)')
->toBeSqlite('(case when 1 = 1 then 2 when 2 = 2 then "val" end)')
->toBeSqlsrv('(case when 1 = 1 then 2 when 2 = 2 then [val] end)');

it('can create create a case-expression with multiple branches and expression default')
->expect(new CaseGroup(
[
new CaseRule(new Expression(2), new ConditionExpression('1 = 1')),
new CaseRule('val', new ConditionExpression('2 = 2')),
],
new Expression('4'),
))
->toBeExecutable(function (Blueprint $table) {
$table->integer('val');
})
->toBeMysql('(case when 1 = 1 then 2 when 2 = 2 then `val` else 4 end)')
->toBePgsql('(case when 1 = 1 then 2 when 2 = 2 then "val" else 4 end)')
->toBeSqlite('(case when 1 = 1 then 2 when 2 = 2 then "val" else 4 end)')
->toBeSqlsrv('(case when 1 = 1 then 2 when 2 = 2 then [val] else 4 end)');

it('can create create a case-expression with multiple branches and column default')
->expect(new CaseGroup(
[
new CaseRule(new Expression(2), new ConditionExpression('1 = 1')),
new CaseRule('val', new ConditionExpression('2 = 2')),
],
'val',
))
->toBeExecutable(function (Blueprint $table) {
$table->integer('val');
})
->toBeMysql('(case when 1 = 1 then 2 when 2 = 2 then `val` else `val` end)')
->toBePgsql('(case when 1 = 1 then 2 when 2 = 2 then "val" else "val" end)')
->toBeSqlite('(case when 1 = 1 then 2 when 2 = 2 then "val" else "val" end)')
->toBeSqlsrv('(case when 1 = 1 then 2 when 2 = 2 then [val] else [val] end)');

0 comments on commit 580a7a0

Please sign in to comment.