provide better abstraction for Condition shorthands
K diskuzi
Při vymýšlení příkladů do dokumentace jsem narazil na několik omezení stávajícího řešení ve chvíli, kdy chci vlastní chování, ale nechci se připravit o tu hezkou zkratku $expr->is()
:
-
SingleCondition
předpokládá, že pořadí operandů bude vždy (1) expression, (2) argument, ale jsou podmínky, kde to může být naopak, jako třeba nalezení prvku v poli (argument = ANY(expression)
). -
BinaryOperation
, který by se jinak dal použít pro 99 % custom operací, předpokládá, že argument je stejného typu jako expression, což ale taky nemusí být vždy pravda (třeba zrovna u hledání podle jsonpath je pattern prostý string, nikoliv jsonb). -
Operation
vlastně vůbec není (matematická) operace, jenom její část, úplnou se stává až spolu s expression, tomuhle celku ale už říkámeCondition
, ne operation. Babylon. - Výsledkem je, že když potom chci napsat nějakou úplně vlastní podmínku, vlastně vůbec nevím, jestli chci implementovat
Condition
, nebo použítSingleCondition
a implementovatOperation
.
Vychází mi z toho, že ta vrstva abstrakce Operation
je tam tak nějak navíc (nebo se přinejmenším špatně jmenuje). Zkusil jsem tu cibuli trochu oloupat tak, že jsem odstranil Operation
a ty jednotlivé operace přepsal do implementací Condition
. Díky tomu:
- Každá
Condition
může vrátit argumenty v potřebném pořadí; - každá
Condition
může explicitně namapovat hodnoty do libovolného typu (ale třeba i nějak odvozeného z typu expression); - a především když teď budu chtít implementovat vlastní filtr, je mi hned jasné, že mám implementovat
Condition
.
Příklad:
<?php
declare(strict_types=1);
use Grifart\Tables\Expression;
use Grifart\Tables\Types\ArrayType;
use function Grifart\Tables\Types\mapToDatabase;
/**
* @template ItemType
*/
final class Contains implements Condition
{
/**
* @param Expression<ItemType[]> $expression
* @param ItemType $value
*/
public function __construct(
private Expression $expression,
private mixed $value,
) {}
public function format(): array
{
// grab the type of the array item
$type = $this->expression->getType();
\assert($type instanceof ArrayType);
$itemType = $type->getItemType();
return [
'? = ANY(?)',
mapToDatabase($this->value, $itemType), // map the value using $itemType
$this->expression->toSql(), // $expression comes second
];
}
}
Ty shorthand funkce pro použití v is()
teď místo Operation
vracejí closure, která dostane expression a vyrobí Condition
. Ještě se nabízí místo closure mít nějaký interface, ale říkal bych mu spíš nějak ve smyslu ConditionFactory
než Operation
:)