186 lines
5.3 KiB
PHP
186 lines
5.3 KiB
PHP
<?php
|
|
|
|
namespace Lucent\Query;
|
|
|
|
use Illuminate\Support\Facades\DB;
|
|
|
|
final class Filter
|
|
{
|
|
private array $operatorsList;
|
|
|
|
public function __construct(
|
|
public array $arguments = [],
|
|
)
|
|
{
|
|
}
|
|
|
|
public function add(array $arguments): Filter
|
|
{
|
|
$this->arguments = $arguments;
|
|
return $this;
|
|
}
|
|
|
|
|
|
private function formatArguments(array $arguments): array
|
|
{
|
|
return (array)collect($arguments)->reduce(fn($c, $v, $k) => $this->formatArgument($c, $v, $k), []);
|
|
}
|
|
|
|
private function formatArgument(array $arguments, mixed $value, string $filter): array
|
|
{
|
|
|
|
$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" => "%{$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 = $this->operatorsList[$operator];
|
|
$arguments[] = [
|
|
"field" => str_replace(".", "->", $field),
|
|
"operator" => $matchedOperator->db,
|
|
"value" => $formattedValue
|
|
];
|
|
|
|
return $arguments;
|
|
}
|
|
|
|
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($this->operatorsList)->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(Query $query): array
|
|
{
|
|
[$arguments, $referenceArguments] = $this->separateMainFromReferenceArguments();
|
|
if (empty($referenceArguments)) {
|
|
return [$arguments, []];
|
|
};
|
|
|
|
$subqueries = collect($referenceArguments)->reduce(function ($c, $v, $k) {
|
|
$keyWithoutRef = str_replace("children.", "", $k);
|
|
[$field] = explode(".", $keyWithoutRef);
|
|
$referenceField = str_replace($field . ".", "", $keyWithoutRef);
|
|
$c[$field][$referenceField] = $v;
|
|
return $c;
|
|
}, []);
|
|
|
|
|
|
$sourceIds = collect($subqueries)->reduce(function ($c, $subquery, $k) use ($query) {
|
|
|
|
$graph = $query->filter($subquery)->run();
|
|
|
|
if (!$graph->hasResults()) {
|
|
return $c;
|
|
}
|
|
|
|
$targetIds = collect($graph->records)->pluck("id");
|
|
$sourceIds = DB::table("edges")->whereIn("target", $targetIds)->where("field", $k)->get()->pluck("source");
|
|
return array_merge($c, $sourceIds->toArray());
|
|
}, []);
|
|
|
|
return [$arguments, [
|
|
"field" => "id",
|
|
"operator" => "in",
|
|
"value" => $sourceIds
|
|
]];
|
|
}
|
|
|
|
private function separateMainFromReferenceArguments(): array
|
|
{
|
|
return collect($this->arguments)->partition(function ($v, $k) {
|
|
if (!str_starts_with($k, "children.")) {
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
})->toArray();
|
|
}
|
|
|
|
|
|
public function run(Query $query): array
|
|
{
|
|
[$argumentsWithoutReferences, $referencesFilter] = $this->formatReferences($query);
|
|
|
|
$this->operatorsList = Operator::list();
|
|
$formattedArguments = $this->formatArguments($argumentsWithoutReferences);
|
|
if (!empty($referencesFilter)) {
|
|
$formattedArguments[] = $referencesFilter;
|
|
}
|
|
return $formattedArguments;
|
|
}
|
|
}
|