Skip to content

Instantly share code, notes, and snippets.

@cizordj
Created July 7, 2025 16:45
Show Gist options
  • Save cizordj/9b2fdf1014fd0fbdcd677de93d64fa83 to your computer and use it in GitHub Desktop.
Save cizordj/9b2fdf1014fd0fbdcd677de93d64fa83 to your computer and use it in GitHub Desktop.
Case builder for Doctrine
<?php
declare(strict_types=1);
interface WhenClauseInterface
{
public function then(string $result): CaseBuilder;
}
use Ds\Vector;
use function implode;
use InvalidArgumentException;
final class CaseBuilder
{
/**
* @var Vector<array{when: string, then: string}> $whenClauses
*/
public readonly Vector $whenClauses;
private ?string $elseClause;
private ?string $caseExpression;
private function __construct()
{
$this->whenClauses = new Vector();
$this->elseClause = null;
$this->caseExpression = null;
}
public static function new(): static
{
return new self();
}
public static function expression(string $expression): static
{
$new = new self();
$new->caseExpression = $expression;
return $new;
}
public function when(string $condition): WhenClauseInterface
{
return new class($this, $condition) implements WhenClauseInterface {
public function __construct(
private CaseBuilder $caseBuilder,
private string $condition
) {}
public function then(string $result): CaseBuilder
{
$new = clone $this->caseBuilder;
$new->whenClauses = $this->caseBuilder->whenClauses->copy();
$new->whenClauses->push(['when' => $this->condition, 'then' => $result]);
return $new;
}
};
}
public function else(string $result): static
{
$new = clone $this;
$new->elseClause = $result;
return $new;
}
public function build(): string
{
if ($this->whenClauses->isEmpty()) {
throw new InvalidArgumentException(
'CASE statement must have at least one WHEN clause'
);
}
$parts = [];
if ($this->caseExpression !== null) {
$parts[] = "CASE {$this->caseExpression}";
} else {
$parts[] = 'CASE';
}
foreach ($this->whenClauses as $clause) {
$parts[] = "WHEN {$clause['when']} THEN '{$clause['then']}'";
}
if ($this->elseClause !== null) {
$parts[] = "ELSE '{$this->elseClause}'";
}
$parts[] = 'END';
return implode(' ', $parts);
}
}
// Usage:
$foo = CaseBuilder::new()
->when('permissao.id = 1')
->then('true')
->else('false')
->build();
// CASE WHEN permission.id = 1 THEN 'true' ELSE 'false' END
$bar = CaseBuilder::expression('permission.id')
->when('1')
->then('Foo')
->when('2')
->then('Bar')
->else('unknown')
->build();
// CASE permission.id WHEN 1 THEN 'Foo' WHEN 2 THEN 'Bar' ELSE 'unknown' END
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment