Files
lucent-laravel/src/Query/Filter.php
T

186 lines
5.3 KiB
PHP
Raw Normal View History

2023-10-02 23:10:49 +03:00
<?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;
}
}