Skip to content

Commit

Permalink
feat: query builder enhancements
Browse files Browse the repository at this point in the history
Signed-off-by: tbreuss <[email protected]>
  • Loading branch information
tbreuss committed Dec 26, 2022
1 parent 901f757 commit 18a64a6
Show file tree
Hide file tree
Showing 7 changed files with 84 additions and 308 deletions.
1 change: 0 additions & 1 deletion plugins/twig/TwigExtension.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@
use herbie\PageTreeTextRenderer;
use herbie\Pagination;
use herbie\QueryBuilder;
use herbie\Selector;
use herbie\Site;
use herbie\Translator;
use herbie\UrlManager;
Expand Down
5 changes: 5 additions & 0 deletions system/PageList.php
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,11 @@ public function find(string $value, string $key): ?Page
return null;
}

public function query(): QueryBuilder
{
return (new QueryBuilder())->from($this);
}

/**
* Run a filter over each of the items.
*
Expand Down
84 changes: 64 additions & 20 deletions system/QueryBuilder.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
final class QueryBuilder implements IteratorAggregate
{
private const WHERE_CLAUSE_OPERATORS = ['AND', 'OR'];
// ordered by key string length
private const OPERATORS = [
"!=" => 'matchNotEqual',
">=" => 'matchGreaterThanEqual',
Expand All @@ -24,10 +25,10 @@ final class QueryBuilder implements IteratorAggregate
"^=" => 'matchStarts',
"~=" => 'matchContainsWords',
"$=" => 'matchEnds',
"&" => 'matchBitwiseAnd',
"=" => 'matchEqual',
">" => 'matchGreaterThan',
"<" => 'matchLessThan',
"=" => 'matchEqual',
"&" => 'matchBitwiseAnd',
];
private array $where;
private int $limit;
Expand Down Expand Up @@ -91,12 +92,45 @@ private function parseCondition(string $condition): array
$position = stripos($condition, $syntax);
if ($position !== false) {
$syntaxLength = strlen($syntax);
return [$name, substr($condition, 0, $position), substr($condition, $position + $syntaxLength)];
$value1 = substr($condition, 0, $position);
$value2 = substr($condition, $position + $syntaxLength);
if (str_contains($value1, '|')) {
$values1 = str_explode_filtered($value1, '|');
$conditions = ['OR'];
foreach ($values1 as $value1) {
$conditions[] = [$name, $value1, $value2];
}
return $conditions;
}
if (str_contains($value2, '|')) {
$values2 = str_explode_filtered($value2, '|');
$conditions = ['OR'];
foreach ($values2 as $value2) {
$conditions[] = [$name, $value1, $value2];
}
return $conditions;
}
return [$name, $value1, $value2];
}
}
throw new \InvalidArgumentException('Unsupported operator');
}

private function convertType(mixed $value1, mixed $value2): mixed
{
if (is_bool($value1) && is_string($value2)) {
$lowered = strtolower($value2);
if ($lowered === 'true') {
return true;
}
if ($lowered === 'false') {
return false;
}
return $value2;
}
return $value2;
}

private function parseConditionsInOperatorFormat(array $conditions): array
{
if (!isset($conditions[0]) || !in_array(strtoupper($conditions[0]), self::WHERE_CLAUSE_OPERATORS)) {
Expand All @@ -119,7 +153,8 @@ private function parseConditionsInHashFormat(array $conditions): array
$items = [];
foreach ($conditions as $key => $value) {
if (is_scalar($value)) {
$items[] = ['match' . ucfirst(gettype($value)), $key, $value];
$type = \herbie\gettype($value);
$items[] = ['match' . ucfirst($type), $key, $value];
}
}
return array_merge(['AND'], $items);
Expand Down Expand Up @@ -149,13 +184,11 @@ public function all(): iterable
return $this->processed;
}

/**
* @return null|mixed
*/
public function one()
public function one(): array|object|null
{
$this->limit = 1;
$this->processData();
$item = reset($this->data);
$item = reset($this->processed);
if ($item === false) {
return null;
}
Expand Down Expand Up @@ -202,13 +235,24 @@ private function processItem(ArrayAccess|array $item, array $conditions): bool
if (isset($condition[0]) && in_array(strtoupper($condition[0]), self::WHERE_CLAUSE_OPERATORS)) {
$status[] = $this->processItem($item, $condition);
} else {
[$operator, $field, $value] = $condition;
[$operator, $field, $value2] = $condition;
if (!isset($item[$field])) {
$status[] = false;
} else {
/** @var callable $callable */
$callable = [$this, $operator];
$status[] = call_user_func_array($callable, [$item[$field], $value]);
$value1 = $item[$field];
if (is_array($value1)) {
$arrStatus = [];
foreach ($value1 as $arrValue1) {
$value2 = $this->convertType($arrValue1, $value2);
$arrStatus[] = call_user_func_array($callable, [$arrValue1, $value2]);
}
$status[] = in_array(true, $arrStatus);
} else {
$value2 = $this->convertType($value1, $value2);
$status[] = call_user_func_array($callable, [$value1, $value2]);
}
}
}
}
Expand Down Expand Up @@ -260,7 +304,7 @@ private function sort(): bool

protected function matchString(string $value1, string $value2): bool
{
return $this->matchEqual($value1, $value2);
return $value1 === $value2;
}

protected function matchBoolean(bool $value1, bool $value2): bool
Expand All @@ -278,39 +322,39 @@ protected function matchFloat(float $value1, float $value2): bool
return $value1 === $value2;
}

protected function matchEqual(string $value1, string $value2): bool
protected function matchEqual(string|float|int|bool $value1, string|float|int|bool $value2): bool
{
return $value1 === $value2;
}

protected function matchNotEqual(string $value1, string $value2): bool
protected function matchNotEqual(string|float|int|bool $value1, string|float|int|bool $value2): bool
{
return $value1 !== $value2;
}

protected function matchGreaterThan(string $value1, string $value2): bool
protected function matchGreaterThan(string|float|int|bool $value1, string|float|int|bool $value2): bool
{
return $value1 > $value2;
}

protected function matchLessThan(string $value1, string $value2): bool
protected function matchLessThan(string|float|int|bool $value1, string|float|int|bool $value2): bool
{
return $value1 < $value2;
}

protected function matchGreaterThanEqual(string $value1, string $value2): bool
protected function matchGreaterThanEqual(string|float|int|bool $value1, string|float|int|bool $value2): bool
{
return $value1 >= $value2;
}

protected function matchLessThanEqual(string $value1, string $value2): bool
protected function matchLessThanEqual(string|float|int|bool $value1, string|float|int|bool $value2): bool
{
return $value1 <= $value2;
}

protected function matchBitwiseAnd(string $value1, string $value2): bool
protected function matchBitwiseAnd(int $value1, int $value2): bool
{
return ((int)$value1 & (int)$value2) > 0;
return ($value1 & $value2) > 0;
}

protected function matchContains(string $value1, string $value2): bool
Expand Down
Loading

0 comments on commit 18a64a6

Please sign in to comment.