refactoring of filters

This commit is contained in:
2024-08-24 17:22:40 +03:00
parent 97ad9de3d2
commit d9e2c4954a
57 changed files with 1175 additions and 349 deletions
+11 -11
View File
@@ -8,7 +8,7 @@ use Lucent\Account\AccountService;
use Lucent\Account\AuthService;
use Lucent\Channel\ChannelService;
use Lucent\LucentException;
use Lucent\Query\Operator;
use Lucent\Query\Operator\OperatorRegistry;
use Lucent\Query\Query;
use Lucent\Record\InputData\EdgeInputData;
use Lucent\Record\InputData\RecordInputData;
@@ -25,13 +25,14 @@ use function Lucent\Response\ok;
class RecordController extends Controller
{
public function __construct(
private readonly RecordService $recordService,
private readonly AccountService $accountService,
private readonly AuthService $authService,
private readonly ChannelService $channelService,
private readonly Svelte $svelte,
private readonly Query $query,
private readonly Manager $recordManager
private readonly RecordService $recordService,
private readonly AccountService $accountService,
private readonly AuthService $authService,
private readonly ChannelService $channelService,
private readonly Svelte $svelte,
private readonly Query $query,
private readonly Manager $recordManager,
private readonly OperatorRegistry $operatorRegistry
)
{
}
@@ -79,7 +80,6 @@ class RecordController extends Controller
$records = $graph->getRootRecords()->toArray();
$data = [
"schemas" => $this->channelService->channel->schemas,
"schema" => $schema,
@@ -87,7 +87,7 @@ class RecordController extends Controller
"records" => $records,
"graph" => toArray($graph),
"systemFields" => array_values(System::list()),
"operators" => array_values(Operator::list()),
"operators" => $this->operatorRegistry->all(),
"sortParam" => $sort,
"sortField" => $schema->fields->merge(array_values(System::list()))->firstWhere(fn($field) => $field->name === $sort || "-" . $field->name === $sort || "data." . $field->name === $sort || "-data." . $field->name === $sort),
"limit" => $limit,
@@ -302,7 +302,7 @@ class RecordController extends Controller
id: $request->input("record.id"),
data: $request->input("record.data"),
status: Status::from($request->input("record.status")),
edges: array_map(EdgeInputData::fromArray(...), $request->input("edges") ?? []),
edges: array_map(EdgeInputData::fromArray(...), $request->input("edges") ?? []),
);
}
@@ -0,0 +1,13 @@
<?php
namespace Lucent\Query\BuilderConverter;
use Lucent\Query\Filter\Argument;
class BuilderConverter
{
public function for(Argument $argument): IBuilderConverter
{
return new ($argument->operator->converter)($argument);
}
}
+29
View File
@@ -0,0 +1,29 @@
<?php
namespace Lucent\Query\BuilderConverter;
use Illuminate\Database\Query\Builder;
use Lucent\Query\Filter\Argument;
readonly class Equals implements IBuilderConverter
{
public function __construct(private Argument $argument)
{
}
public function toAndQueryBuilder(Builder $builder): Builder
{
return $builder->where($this->argument->field, $this->formatValue());
}
public function toOrQueryBuilder(Builder $builder): Builder
{
return $builder->orWhere($this->argument->field, $this->formatValue());
}
private function formatValue(): string
{
return trim($this->argument->value);
}
}
@@ -0,0 +1,26 @@
<?php
namespace Lucent\Query\BuilderConverter;
use Illuminate\Database\Query\Builder;
use Lucent\Query\Filter\Argument;
readonly class EqualsFalse implements IBuilderConverter
{
public function __construct(private Argument $argument)
{
}
public function toAndQueryBuilder(Builder $builder): Builder
{
return $builder->where($this->argument->field, false);
}
public function toOrQueryBuilder(Builder $builder): Builder
{
return $builder->orWhere($this->argument->field, false);
}
}
@@ -0,0 +1,31 @@
<?php
namespace Lucent\Query\BuilderConverter;
use Illuminate\Database\Query\Builder;
use Lucent\Query\Filter\Argument;
readonly class EqualsNumber implements IBuilderConverter
{
public function __construct(private Argument $argument)
{
}
public function toAndQueryBuilder(Builder $builder): Builder
{
return $builder->where($this->argument->field, $this->formatValue());
}
public function toOrQueryBuilder(Builder $builder): Builder
{
return $builder->orWhere($this->argument->field, $this->formatValue());
}
private function formatValue(): int|float
{
$value = trim($this->argument->value);
return str_contains($value, ".") ? floatval($value) : intval($value);
}
}
+25
View File
@@ -0,0 +1,25 @@
<?php
namespace Lucent\Query\BuilderConverter;
use Illuminate\Database\Query\Builder;
use Lucent\Query\Filter\Argument;
readonly class EqualsTrue implements IBuilderConverter
{
public function __construct(private Argument $argument)
{
}
public function toAndQueryBuilder(Builder $builder): Builder
{
return $builder->where($this->argument->field, true);
}
public function toOrQueryBuilder(Builder $builder): Builder
{
return $builder->orWhere($this->argument->field, true);
}
}
+25
View File
@@ -0,0 +1,25 @@
<?php
namespace Lucent\Query\BuilderConverter;
use Illuminate\Database\Query\Builder;
use Lucent\Query\Filter\Argument;
readonly class Exists implements IBuilderConverter
{
public function __construct(private Argument $argument)
{
}
public function toAndQueryBuilder(Builder $builder): Builder
{
return $builder->whereNot($this->argument->field, "")->whereNotNull($this->argument->field);
}
public function toOrQueryBuilder(Builder $builder): Builder
{
return $builder->whereNot($this->argument->field, "")->whereNotNull($this->argument->field);
}
}
+25
View File
@@ -0,0 +1,25 @@
<?php
namespace Lucent\Query\BuilderConverter;
use Illuminate\Database\Query\Builder;
use Lucent\Query\Filter\Argument;
readonly class Filter implements IBuilderConverter
{
public function __construct(private Argument $argument)
{
}
public function toAndQueryBuilder(Builder $builder): Builder
{
return $builder->whereJsonContains($this->argument->field, [$this->argument->value]);
}
public function toOrQueryBuilder(Builder $builder): Builder
{
return $builder->orWhereJsonContains($this->argument->field, [$this->argument->value]);
}
}
@@ -0,0 +1,35 @@
<?php
namespace Lucent\Query\BuilderConverter;
use Illuminate\Database\Query\Builder;
use Lucent\Query\Filter\Argument;
readonly class GreaterThan implements IBuilderConverter
{
public function __construct(private Argument $argument)
{
}
public function toAndQueryBuilder(Builder $builder): Builder
{
return $builder->where($this->argument->field, ">", $this->formatValue());
}
public function toOrQueryBuilder(Builder $builder): Builder
{
return $builder->orWhere($this->argument->field, ">", $this->formatValue());
}
private function formatValue(): int|float
{
$value = trim($this->argument->value);
if (is_numeric($value)) {
return str_contains($value, ".") ? floatval($value) : intval($value);
}
return $value;
}
}
@@ -0,0 +1,35 @@
<?php
namespace Lucent\Query\BuilderConverter;
use Illuminate\Database\Query\Builder;
use Lucent\Query\Filter\Argument;
readonly class GreaterThanEquals implements IBuilderConverter
{
public function __construct(private Argument $argument)
{
}
public function toAndQueryBuilder(Builder $builder): Builder
{
return $builder->where($this->argument->field, ">=", $this->formatValue());
}
public function toOrQueryBuilder(Builder $builder): Builder
{
return $builder->orWhere($this->argument->field, ">=", $this->formatValue());
}
private function formatValue(): int|float
{
$value = trim($this->argument->value);
if (is_numeric($value)) {
return str_contains($value, ".") ? floatval($value) : intval($value);
}
return $value;
}
}
@@ -0,0 +1,13 @@
<?php
namespace Lucent\Query\BuilderConverter;
use Illuminate\Database\Query\Builder;
interface IBuilderConverter
{
public function toAndQueryBuilder(Builder $builder): Builder;
public function toOrQueryBuilder(Builder $builder): Builder;
}
+33
View File
@@ -0,0 +1,33 @@
<?php
namespace Lucent\Query\BuilderConverter;
use Illuminate\Database\Query\Builder;
use Lucent\Query\Filter\Argument;
readonly class In implements IBuilderConverter
{
public function __construct(private Argument $argument)
{
}
public function toAndQueryBuilder(Builder $builder): Builder
{
return $builder->whereIn($this->argument->field, $this->formatValue());
}
public function toOrQueryBuilder(Builder $builder): Builder
{
return $builder->orWhereIn($this->argument->field, $this->formatValue());
}
private function formatValue(): array
{
$value = $this->argument->value;
if (is_string($value)) {
$value = explode(",", $value);
}
return array_map(fn($v) => trim($v), $value);
}
}
+39
View File
@@ -0,0 +1,39 @@
<?php
namespace Lucent\Query\BuilderConverter;
use Illuminate\Database\Query\Builder;
use Lucent\Query\Filter\Argument;
readonly class InNum implements IBuilderConverter
{
public function __construct(private Argument $argument)
{
}
public function toAndQueryBuilder(Builder $builder): Builder
{
return $builder->whereIn($this->argument->field, $this->formatValue());
}
public function toOrQueryBuilder(Builder $builder): Builder
{
return $builder->orWhereIn($this->argument->field, $this->formatValue());
}
private function formatValue(): array
{
$value = $this->argument->value;
if (is_string($value)) {
$value = explode(",", $value);
}
return array_map(fn($v) => $this->formatNumber($v), $value);
}
private function formatNumber(string $value): float|int
{
$value = trim($value);
return str_contains($value, ".") ? floatval($value) : intval($value);
}
}
+25
View File
@@ -0,0 +1,25 @@
<?php
namespace Lucent\Query\BuilderConverter;
use Illuminate\Database\Query\Builder;
use Lucent\Query\Filter\Argument;
readonly class IsNull implements IBuilderConverter
{
public function __construct(private Argument $argument)
{
}
public function toAndQueryBuilder(Builder $builder): Builder
{
return $builder->whereNull($this->argument->field);
}
public function toOrQueryBuilder(Builder $builder): Builder
{
return $builder->orWhereNull($this->argument->field);
}
}
+35
View File
@@ -0,0 +1,35 @@
<?php
namespace Lucent\Query\BuilderConverter;
use Illuminate\Database\Query\Builder;
use Lucent\Query\Filter\Argument;
readonly class LessThan implements IBuilderConverter
{
public function __construct(private Argument $argument)
{
}
public function toAndQueryBuilder(Builder $builder): Builder
{
return $builder->where($this->argument->field, "<", $this->formatValue());
}
public function toOrQueryBuilder(Builder $builder): Builder
{
return $builder->orWhere($this->argument->field, "<", $this->formatValue());
}
private function formatValue(): int|float
{
$value = trim($this->argument->value);
if (is_numeric($value)) {
return str_contains($value, ".") ? floatval($value) : intval($value);
}
return $value;
}
}
@@ -0,0 +1,35 @@
<?php
namespace Lucent\Query\BuilderConverter;
use Illuminate\Database\Query\Builder;
use Lucent\Query\Filter\Argument;
readonly class LessThanEquals implements IBuilderConverter
{
public function __construct(private Argument $argument)
{
}
public function toAndQueryBuilder(Builder $builder): Builder
{
return $builder->where($this->argument->field, "<=", $this->formatValue());
}
public function toOrQueryBuilder(Builder $builder): Builder
{
return $builder->orWhere($this->argument->field, "<=", $this->formatValue());
}
private function formatValue(): int|float
{
$value = trim($this->argument->value);
if (is_numeric($value)) {
return str_contains($value, ".") ? floatval($value) : intval($value);
}
return $value;
}
}
+29
View File
@@ -0,0 +1,29 @@
<?php
namespace Lucent\Query\BuilderConverter;
use Illuminate\Database\Query\Builder;
use Lucent\Query\Filter\Argument;
readonly class NotEquals implements IBuilderConverter
{
public function __construct(private Argument $argument)
{
}
public function toAndQueryBuilder(Builder $builder): Builder
{
return $builder->whereNot($this->argument->field, $this->formatValue());
}
public function toOrQueryBuilder(Builder $builder): Builder
{
return $builder->orWhereNot($this->argument->field, $this->formatValue());
}
private function formatValue(): string
{
return trim($this->argument->value);
}
}
@@ -0,0 +1,26 @@
<?php
namespace Lucent\Query\BuilderConverter;
use Illuminate\Database\Query\Builder;
use Lucent\Query\Filter\Argument;
readonly class NotEqualsFalse implements IBuilderConverter
{
public function __construct(private Argument $argument)
{
}
public function toAndQueryBuilder(Builder $builder): Builder
{
return $builder->whereNot($this->argument->field, false);
}
public function toOrQueryBuilder(Builder $builder): Builder
{
return $builder->orWhereNot($this->argument->field, false);
}
}
@@ -0,0 +1,30 @@
<?php
namespace Lucent\Query\BuilderConverter;
use Illuminate\Database\Query\Builder;
use Lucent\Query\Filter\Argument;
readonly class NotEqualsNumber implements IBuilderConverter
{
public function __construct(private Argument $argument)
{
}
public function toAndQueryBuilder(Builder $builder): Builder
{
return $builder->whereNot($this->argument->field, $this->formatValue());
}
public function toOrQueryBuilder(Builder $builder): Builder
{
return $builder->orWhereNot($this->argument->field, $this->formatValue());
}
private function formatValue(): int|float
{
$value = trim($this->argument->value);
return str_contains($value, ".") ? floatval($value) : intval($value);
}
}
@@ -0,0 +1,25 @@
<?php
namespace Lucent\Query\BuilderConverter;
use Illuminate\Database\Query\Builder;
use Lucent\Query\Filter\Argument;
readonly class NotEqualsTrue implements IBuilderConverter
{
public function __construct(private Argument $argument)
{
}
public function toAndQueryBuilder(Builder $builder): Builder
{
return $builder->whereNot($this->argument->field, true);
}
public function toOrQueryBuilder(Builder $builder): Builder
{
return $builder->orWhereNot($this->argument->field, true);
}
}
+25
View File
@@ -0,0 +1,25 @@
<?php
namespace Lucent\Query\BuilderConverter;
use Illuminate\Database\Query\Builder;
use Lucent\Query\Filter\Argument;
readonly class NotExists implements IBuilderConverter
{
public function __construct(private Argument $argument)
{
}
public function toAndQueryBuilder(Builder $builder): Builder
{
return $builder->where(fn($b) => $b->where($this->argument->field, "")->orWhereNull($this->argument->field));
}
public function toOrQueryBuilder(Builder $builder): Builder
{
return $builder->orWhere(fn($b) => $b->where($this->argument->field, "")->orWhereNull($this->argument->field));
}
}
+33
View File
@@ -0,0 +1,33 @@
<?php
namespace Lucent\Query\BuilderConverter;
use Illuminate\Database\Query\Builder;
use Lucent\Query\Filter\Argument;
readonly class NotIn implements IBuilderConverter
{
public function __construct(private Argument $argument)
{
}
public function toAndQueryBuilder(Builder $builder): Builder
{
return $builder->whereNotIn($this->argument->field, $this->formatValue());
}
public function toOrQueryBuilder(Builder $builder): Builder
{
return $builder->orWhereNotIn($this->argument->field, $this->formatValue());
}
private function formatValue(): array
{
$value = $this->argument->value;
if (is_string($value)) {
$value = explode(",", $value);
}
return array_map(fn($v) => trim($v), $value);
}
}
+39
View File
@@ -0,0 +1,39 @@
<?php
namespace Lucent\Query\BuilderConverter;
use Illuminate\Database\Query\Builder;
use Lucent\Query\Filter\Argument;
readonly class NotInNum implements IBuilderConverter
{
public function __construct(private Argument $argument)
{
}
public function toAndQueryBuilder(Builder $builder): Builder
{
return $builder->whereNotIn($this->argument->field, $this->formatValue());
}
public function toOrQueryBuilder(Builder $builder): Builder
{
return $builder->orWhereNotIn($this->argument->field, $this->formatValue());
}
private function formatValue(): array
{
$value = $this->argument->value;
if (is_string($value)) {
$value = explode(",", $value);
}
return array_map(fn($v) => $this->formatNumber($v), $value);
}
private function formatNumber(string $value): float|int
{
$value = trim($value);
return str_contains($value, ".") ? floatval($value) : intval($value);
}
}
+25
View File
@@ -0,0 +1,25 @@
<?php
namespace Lucent\Query\BuilderConverter;
use Illuminate\Database\Query\Builder;
use Lucent\Query\Filter\Argument;
readonly class NotNull implements IBuilderConverter
{
public function __construct(private Argument $argument)
{
}
public function toAndQueryBuilder(Builder $builder): Builder
{
return $builder->whereNotNull($this->argument->field);
}
public function toOrQueryBuilder(Builder $builder): Builder
{
return $builder->orWhereNotNull($this->argument->field);
}
}
+30
View File
@@ -0,0 +1,30 @@
<?php
namespace Lucent\Query\BuilderConverter;
use Illuminate\Database\Query\Builder;
use Lucent\Query\Filter\Argument;
readonly class Regex implements IBuilderConverter
{
public function __construct(private Argument $argument)
{
}
public function toAndQueryBuilder(Builder $builder): Builder
{
return $builder->where($this->argument->field, "like", $this->formatValue());
}
public function toOrQueryBuilder(Builder $builder): Builder
{
return $builder->orWhere($this->argument->field, "like", $this->formatValue());
}
private function formatValue(): string
{
return "%" . strtolower(trim($this->argument->value)) . "%";
}
}
+8 -4
View File
@@ -2,11 +2,15 @@
namespace Lucent\Query\Filter;
use Lucent\Query\Operator\Operator;
class Argument
{
public function __construct(
public string $field,
public string $operator,
public mixed $value,
){}
public string $field,
public Operator $operator,
public mixed $value,
)
{
}
}
+16 -139
View File
@@ -5,19 +5,26 @@ namespace Lucent\Query;
use Illuminate\Contracts\Foundation\Application;
use Illuminate\Database\Query\Builder;
use Illuminate\Support\Facades\DB;
use Lucent\Query\BuilderConverter\BuilderConverter;
use Lucent\Query\Filter\AndFilter;
use Lucent\Query\Filter\Argument;
use Lucent\Query\Filter\Filter;
use Lucent\Query\Filter\OrFilter;
use Lucent\Query\Operator\In;
use Lucent\Query\Operator\OperatorDetector;
use function explode;
final class FilterParser
{
public function __construct(public Application $app)
public function __construct(
public Application $app,
public BuilderConverter $builderConverter,
public OperatorDetector $operatorDetector,
)
{
}
/**
* @param array $arguments
* @return array<Argument>
@@ -25,105 +32,11 @@ final class FilterParser
private function formatArguments(array $arguments): array
{
return collect($arguments)->reduce(function ($c, $v, $k) {
$c[] = $this->formatArgument($v, $k);
$c[] = $this->operatorDetector->detect($v, $k);
return $c;
}, []);
}
private function formatArgument(mixed $value, string $filter): Argument
{
$operator = $this->detectOperator($filter);
$field = $this->detectField($filter, $operator);
$formattedValue = match ($operator) {
"eq" => $this->formatText($value),
"ne" => $this->formatText($value),
"eqnum" => $this->formatNumber($value),
"nenum" => $this->formatNumber($value),
"object" => $this->formatText($value),
"in" => $this->formatListString($value),
"nin" => $this->formatListString($value),
"innum" => $this->formatListNum($value),
"ninnum" => $this->formatListNum($value),
"eqtrue" => true,
"eqfalse" => false,
"netrue" => true,
"nefalse" => false,
"regex" => "%" . strtolower($value) . "%",
"gt" => is_numeric($value) ? floatval($value) : $value,
"gte" => is_numeric($value) ? floatval($value) : $value,
"lt" => is_numeric($value) ? floatval($value) : $value,
"lte" => is_numeric($value) ? floatval($value) : $value,
"null" => null,
"nnull" => null,
"exists" => true,
"nexists" => false,
default => $value,
};
$matchedOperator = Operator::list()[$operator];
return new Argument(
field: str_replace(".", "->", $field),
operator: $matchedOperator->db,
value: $formattedValue
);
}
private function formatText(string $value): string
{
return trim($value);
}
private function formatNumber(string $value): float
{
return floatval($value);
}
private function formatListString(mixed $value): array
{
if (is_string($value)) {
$value = explode(",", $value);
}
return array_map(fn($v) => $this->formatText($v), $value);
}
private function formatListNum(mixed $value): array
{
if (\is_string($value)) {
$value = explode(",", $value);
}
return \array_map(fn($v) => $this->formatNumber($v), $value);
}
private function detectOperator(string $filter): string
{
$exploded = \explode("_", $filter);
$candidate = end($exploded);
$operatorsListNames = collect(Operator::list())->map(fn($o) => $o->name)->toArray();
if (\in_array($candidate, $operatorsListNames)) {
return $candidate;
}
return 'eq';
}
private function detectField(string $filter, string $operator): string
{
$exploded = explode("_", $filter);
$candidate = array_pop($exploded);
if ($candidate === $operator) {
return implode("_", $exploded);
}
return $filter;
}
private function formatReferences(array $referenceArguments): Argument
{
$subqueries = collect($referenceArguments)->reduce(function ($c, $v, $k) {
@@ -151,7 +64,7 @@ final class FilterParser
return new Argument(
field: "id",
operator: "in",
operator: In::make(),
value: $sourceIds
);
@@ -174,7 +87,6 @@ final class FilterParser
private function parseArguments(Filter $arguments): array
{
[$normalArguments, $referenceArguments] = $this->separateMainFromReferenceArguments($arguments);
$formattedArguments = $this->formatArguments($normalArguments);
if (!empty($referenceArguments)) {
$formattedArguments[] = $this->formatReferences($referenceArguments);
@@ -198,26 +110,8 @@ final class FilterParser
*/
private function parseAnd(Builder $builder, array $arguments): Builder
{
foreach ($arguments as $argument) {
if ($argument->operator == "in") {
$builder->whereIn($argument->field, $argument->value);
} else if ($argument->operator == "nin") {
$builder->whereNotIn($argument->field, $argument->value);
} else if ($argument->operator == "exists") {
$builder->where($argument->field, "!=", "");
$builder->where($argument->field, "!=", null);
} elseif ($argument->operator == "filter") {
$builder->whereJsonContains($argument->field, [$argument->value]);
// target result
// filter[data.previousNames_object]=previousNames&filter[previousNames.name_eq]=alpha&filter[previousNames.id_eqnum]=24
// $query->whereJsonContains("data->previousNames", [["name" => "alpha", "id" => 24]]);
// $query->whereJsonContains($filter["field"], [$objectFilters]);
} else {
$builder->where($argument->field, $argument->operator, $argument->value);
}
}
return $builder;
return collect($arguments)
->reduce(fn($cBuilder, Argument $arg) => $this->builderConverter->for($arg)->toAndQueryBuilder($cBuilder), $builder);
}
/**
@@ -225,27 +119,10 @@ final class FilterParser
*/
private function parseOr(Builder $builder, array $arguments): Builder
{
$builder->where(function (Builder $orBuilder) use ($arguments) {
foreach ($arguments as $argument) {
if ($argument->operator == "in") {
$orBuilder->orWhereIn($argument->field, $argument->value);
} else if ($argument->operator == "nin") {
$orBuilder->orWhereNotIn($argument->field, $argument->value);
} else if ($argument->operator == "exists") {
$orBuilder->where($argument->field, "!=", "");
$orBuilder->where($argument->field, "!=", null);
} elseif ($argument->operator == "filter") {
$orBuilder->whereJsonContains($argument->field, [$argument->value]);
// target result
// filter[data.previousNames_object]=previousNames&filter[previousNames.name_eq]=alpha&filter[previousNames.id_eqnum]=24
// $query->whereJsonContains("data->previousNames", [["name" => "alpha", "id" => 24]]);
// $query->whereJsonContains($filter["field"], [$objectFilters]);
} else {
$orBuilder->orWhere($argument->field, $argument->operator, $argument->value);
}
}
return $builder->where(function (Builder $orBuilder) use ($arguments) {
return collect($arguments)
->reduce(fn($cBuilder, Argument $arg) => $this->builderConverter->for($arg)->toOrQueryBuilder($cBuilder), $orBuilder);
});
return $builder;
}
-186
View File
@@ -1,186 +0,0 @@
<?php
namespace Lucent\Query;
final class Operator
{
/**
* @psalm-param string[] $uis
*/
public function __construct(
public string $name,
public string $label,
public string $symbol,
public string $db,
public array $uis,
)
{
}
/**
* @return array<string, Operator>
*/
public static function list(): array
{
return [
"regex" => new Operator(
name: "regex",
label: "Search",
symbol: "~",
db: 'like',
uis: ["id", "text", "textarea", "url", "color", "date", "datetime"],
),
"eq" => new Operator(
name: "eq",
label: "Equals",
symbol: "is",
db: '=',
uis: ["id", "text", "textarea", "url", "color", "date", "datetime", "reference"],
),
"ne" => new Operator(
name: "ne",
label: "Not Equals",
symbol: "is not",
db: '!=',
uis: ["id", "text", "textarea", "url", "color", "date", "datetime"],
),
"eqnum" => new Operator(
name: "eqnum",
label: "Equals number",
symbol: "is",
db: '=',
uis: ["number"],
),
"neqnum" => new Operator(
name: "nenum",
label: "Not Equals number",
symbol: "is not",
db: '$ne',
uis: ["number"],
),
"filter" => new Operator(
name: "filter",
label: "Equals Object",
symbol: "is",
db: 'filter',
uis: [],
),
"eqtrue" => new Operator(
name: "eqtrue",
label: "Equals true",
symbol: "is",
db: '=',
uis: ["checkbox"],
),
"eqfalse" => new Operator(
name: "eqfalse",
label: "Equals false",
symbol: "is not",
db: '=',
uis: ["checkbox"],
),
"netrue" => new Operator(
name: "netrue",
label: "Not equals true",
symbol: "!=",
db: '$ne',
uis: ["checkbox"],
),
"nefalse" => new Operator(
name: "nefalse",
label: "Not equals false",
symbol: "!=",
db: '$ne',
uis: ["checkbox"],
),
"in" => new Operator(
name: "in",
label: "In list",
symbol: "in",
db: 'in',
uis: ["id", "text", "textarea", "url", "color", "date", "datetime"],
),
"innum" => new Operator(
name: "innum",
label: "In list of numbers",
symbol: "in",
db: '$in',
uis: ["number"],
),
"nin" => new Operator(
name: "nin",
label: "Not in list",
symbol: "not in",
db: 'nin',
uis: ["id", "text", "textarea", "url", "color", "date", "datetime"],
),
"ninnum" => new Operator(
name: "ninnum",
label: "Not In list of numbers",
symbol: "not in",
db: '$nin',
uis: ["number"],
),
"lt" => new Operator(
name: "lt",
label: "Less than",
symbol: "<",
db: '<',
uis: ["number", "date", "datetime"],
),
"lte" => new Operator(
name: "lte",
label: "Less than equals",
symbol: "<=",
db: '<=',
uis: ["number", "date", "datetime"],
),
"gt" => new Operator(
name: "gt",
label: "Greater than",
symbol: ">",
db: '>',
uis: ["number", "date", "datetime"],
),
"gte" => new Operator(
name: "gte",
label: "Greater than equals",
symbol: ">=",
db: '>=',
uis: ["number", "date", "datetime"],
),
"null" => new Operator(
name: "null",
label: "Is null",
symbol: "=",
db: '$eq',
uis: ["*"],
),
"nnull" => new Operator(
name: "nnull",
label: "Not null",
symbol: "!=",
db: '$ne',
uis: ["*"],
),
"exists" => new Operator(
name: "exists",
label: "Exists",
symbol: "exists",
db: 'exists',
uis: ["*"],
),
"nexists" => new Operator(
name: "nexists",
label: "Not exists",
symbol: "not exists",
db: '$exists',
uis: ["*"],
),
];
}
}
+15
View File
@@ -0,0 +1,15 @@
<?php
namespace Lucent\Query\Operator;
class Equals extends Operator
{
public string $name = "eq";
public string $label = "Equals";
public string $symbol = "is";
public array $uis = ["id", "text", "textarea", "url", "color", "date", "datetime", "reference"];
public bool $hasValue = true;
public string $converter = \Lucent\Query\BuilderConverter\Equals::class;
}
+15
View File
@@ -0,0 +1,15 @@
<?php
namespace Lucent\Query\Operator;
class EqualsFalse extends Operator
{
public string $name = "eqfalse";
public string $label = "Equals False";
public string $symbol = "is";
public array $uis = ["checkbox"];
public bool $hasValue = false;
public string $converter = \Lucent\Query\BuilderConverter\EqualsFalse::class;
}
+15
View File
@@ -0,0 +1,15 @@
<?php
namespace Lucent\Query\Operator;
class EqualsNumber extends Operator
{
public string $name = "eqnum";
public string $label = "Equals Number";
public string $symbol = "is";
public array $uis = ["number"];
public bool $hasValue = true;
public string $converter = \Lucent\Query\BuilderConverter\EqualsNumber::class;
}
+15
View File
@@ -0,0 +1,15 @@
<?php
namespace Lucent\Query\Operator;
class EqualsTrue extends Operator
{
public string $name = "eqtrue";
public string $label = "Equals True";
public string $symbol = "is";
public array $uis = ["checkbox"];
public bool $hasValue = false;
public string $converter = \Lucent\Query\BuilderConverter\EqualsTrue::class;
}
+15
View File
@@ -0,0 +1,15 @@
<?php
namespace Lucent\Query\Operator;
class Exists extends Operator
{
public string $name = "exists";
public string $label = "Exists";
public string $symbol = "exists";
public array $uis = ["*"];
public bool $hasValue = true;
public string $converter = \Lucent\Query\BuilderConverter\Exists::class;
}
+14
View File
@@ -0,0 +1,14 @@
<?php
namespace Lucent\Query\Operator;
class Filter extends Operator
{
public string $name = "filter";
public string $label = "Equals Object";
public string $symbol = "is";
public array $uis = [];
public bool $hasValue = true;
public string $converter = \Lucent\Query\BuilderConverter\Filter::class;
}
+15
View File
@@ -0,0 +1,15 @@
<?php
namespace Lucent\Query\Operator;
class GreaterThan extends Operator
{
public string $name = "gt";
public string $label = "Greater than";
public string $symbol = ">";
public array $uis = ["number", "date", "datetime"];
public bool $hasValue = true;
public string $converter = \Lucent\Query\BuilderConverter\GreaterThan::class;
}
+15
View File
@@ -0,0 +1,15 @@
<?php
namespace Lucent\Query\Operator;
class GreaterThanEquals extends Operator
{
public string $name = "gte";
public string $label = "Greater than equals";
public string $symbol = ">";
public array $uis = ["number", "date", "datetime"];
public bool $hasValue = true;
public string $converter = \Lucent\Query\BuilderConverter\GreaterThanEquals::class;
}
+14
View File
@@ -0,0 +1,14 @@
<?php
namespace Lucent\Query\Operator;
class In extends Operator
{
public string $name = "in";
public string $label = "In list";
public string $symbol = "in";
public array $uis = ["id", "text", "textarea", "url", "color", "date", "datetime"];
public bool $hasValue = true;
public string $converter = \Lucent\Query\BuilderConverter\In::class;
}
+14
View File
@@ -0,0 +1,14 @@
<?php
namespace Lucent\Query\Operator;
class InNum extends Operator
{
public string $name = "innum";
public string $label = "In number list";
public string $symbol = "in";
public array $uis = ["number"];
public bool $hasValue = true;
public string $converter = \Lucent\Query\BuilderConverter\NotInNum::class;
}
+15
View File
@@ -0,0 +1,15 @@
<?php
namespace Lucent\Query\Operator;
class IsNull extends Operator
{
public string $name = "null";
public string $label = "Is Null";
public string $symbol = "is null";
public array $uis = ["*"];
public bool $hasValue = false;
public string $converter = \Lucent\Query\BuilderConverter\IsNull::class;
}
+15
View File
@@ -0,0 +1,15 @@
<?php
namespace Lucent\Query\Operator;
class LessThan extends Operator
{
public string $name = "lt";
public string $label = "Less than";
public string $symbol = ">";
public array $uis = ["number", "date", "datetime"];
public bool $hasValue = true;
public string $converter = \Lucent\Query\BuilderConverter\LessThan::class;
}
+15
View File
@@ -0,0 +1,15 @@
<?php
namespace Lucent\Query\Operator;
class LessThanEquals extends Operator
{
public string $name = "lte";
public string $label = "Less than equals";
public string $symbol = ">";
public array $uis = ["number", "date", "datetime"];
public bool $hasValue = true;
public string $converter = \Lucent\Query\BuilderConverter\LessThanEquals::class;
}
+14
View File
@@ -0,0 +1,14 @@
<?php
namespace Lucent\Query\Operator;
class NotEquals extends Operator
{
public string $name = "ne";
public string $label = "Not Equals";
public string $symbol = "is not";
public array $uis = ["id", "text", "textarea", "url", "color", "date", "datetime", "reference"];
public bool $hasValue = true;
public string $converter = \Lucent\Query\BuilderConverter\NotEquals::class;
}
+15
View File
@@ -0,0 +1,15 @@
<?php
namespace Lucent\Query\Operator;
class NotEqualsFalse extends Operator
{
public string $name = "nefalse";
public string $label = "Not Equals False";
public string $symbol = "is";
public array $uis = ["checkbox"];
public bool $hasValue = false;
public string $converter = \Lucent\Query\BuilderConverter\NotEqualsFalse::class;
}
+15
View File
@@ -0,0 +1,15 @@
<?php
namespace Lucent\Query\Operator;
class NotEqualsNumber extends Operator
{
public string $name = "neqnum";
public string $label = "Not Equals Number";
public string $symbol = "is";
public array $uis = ["number"];
public bool $hasValue = true;
public string $converter = \Lucent\Query\BuilderConverter\NotEqualsNumber::class;
}
+15
View File
@@ -0,0 +1,15 @@
<?php
namespace Lucent\Query\Operator;
class NotEqualsTrue extends Operator
{
public string $name = "netrue";
public string $label = "Not Equals True";
public string $symbol = "is";
public array $uis = ["checkbox"];
public bool $hasValue = false;
public string $converter = \Lucent\Query\BuilderConverter\NotEqualsTrue::class;
}
+15
View File
@@ -0,0 +1,15 @@
<?php
namespace Lucent\Query\Operator;
class NotExists extends Operator
{
public string $name = "nexists";
public string $label = "Not Exists";
public string $symbol = "not exists";
public array $uis = ["*"];
public bool $hasValue = true;
public string $converter = \Lucent\Query\BuilderConverter\NotExists::class;
}
+14
View File
@@ -0,0 +1,14 @@
<?php
namespace Lucent\Query\Operator;
class NotIn extends Operator
{
public string $name = "nin";
public string $label = "Not in list";
public string $symbol = "in";
public array $uis = ["id", "text", "textarea", "url", "color", "date", "datetime"];
public bool $hasValue = true;
public string $converter = \Lucent\Query\BuilderConverter\NotIn::class;
}
+14
View File
@@ -0,0 +1,14 @@
<?php
namespace Lucent\Query\Operator;
class NotInNum extends Operator
{
public string $name = "ninnum";
public string $label = "Not in number list";
public string $symbol = "not in";
public array $uis = ["number"];
public bool $hasValue = true;
public string $converter = \Lucent\Query\BuilderConverter\InNum::class;
}
+15
View File
@@ -0,0 +1,15 @@
<?php
namespace Lucent\Query\Operator;
class NotNull extends Operator
{
public string $name = "nnull";
public string $label = "Is not Null";
public string $symbol = "is not null";
public array $uis = ["*"];
public bool $hasValue = false;
public string $converter = \Lucent\Query\BuilderConverter\NotNull::class;
}
+23
View File
@@ -0,0 +1,23 @@
<?php
namespace Lucent\Query\Operator;
abstract class Operator
{
public string $name;
public string $label;
public string $symbol;
public array $uis;
public bool $hasValue;
/**
* @var class-string
*/
public string $converter;
public static function make(): Operator
{
return new static();
}
}
+42
View File
@@ -0,0 +1,42 @@
<?php
namespace Lucent\Query\Operator;
use Lucent\Query\Filter\Argument;
class OperatorDetector
{
public function __construct(
public OperatorRegistry $operatorRegistry
)
{
}
public function detect(mixed $value, string $filter): Argument
{
$operator = $this->detectOperator($filter);
$field = $this->detectField($filter, $operator->name);
return new Argument(
field: str_replace(".", "->", $field),
operator: $operator,
value: $value
);
}
private function detectOperator(string $filter): Operator
{
$exploded = explode("_", $filter);
$candidate = end($exploded);
return $this->operatorRegistry->matchByName($candidate);
}
private function detectField(string $filter, string $operator): string
{
$exploded = explode("_", $filter);
$candidate = array_pop($exploded);
if ($candidate === $operator) {
return implode("_", $exploded);
}
return $filter;
}
}
+49
View File
@@ -0,0 +1,49 @@
<?php
namespace Lucent\Query\Operator;
final class OperatorRegistry
{
/**
* @return list<Operator>
*/
public function all(): array
{
return [
new Equals(),
new NotEquals(),
new EqualsNumber(),
new NotEqualsNumber(),
new EqualsTrue(),
new EqualsFalse(),
new NotEqualsFalse(),
new NotEqualsTrue(),
new Regex(),
new In(),
new NotIn(),
new InNum(),
new NotInNum(),
new GreaterThan(),
new GreaterThanEquals(),
new LessThan(),
new LessThanEquals(),
new IsNull(),
new NotNull(),
new Exists(),
new NotExists(),
];
}
public function matchByName(string $name): Operator
{
$operator = collect($this->all())->where('name', $name)->first();
if (empty($operator)) {
return new Equals();
}
return $operator;
}
}
+15
View File
@@ -0,0 +1,15 @@
<?php
namespace Lucent\Query\Operator;
class Regex extends Operator
{
public string $name = "regex";
public string $label = "Search";
public string $symbol = "~";
public array $uis = ["id", "text", "textarea", "url", "color", "date", "datetime"];
public bool $hasValue = true;
public string $converter = \Lucent\Query\BuilderConverter\Regex::class;
}
+2 -3
View File
@@ -161,7 +161,7 @@ final class Query
private function findNotLinked(Builder $query): Builder
{
if(empty($this->options->notLinked)){
if (empty($this->options->notLinked)) {
return $query;
}
@@ -169,8 +169,7 @@ final class Query
$query
->select("records.*")
->join('edges', 'records.id', '=', 'edges.target', 'left outer')
->whereNull("edges.target")
;
->whereNull("edges.target");
return $query;
}