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

130 lines
3.8 KiB
PHP
Raw Normal View History

2023-10-13 21:06:23 +03:00
<?php
namespace Lucent\Query;
use Illuminate\Contracts\Foundation\Application;
use Illuminate\Database\Query\Builder;
use Illuminate\Support\Facades\DB;
2024-08-24 17:22:40 +03:00
use Lucent\Query\BuilderConverter\BuilderConverter;
2023-10-13 21:06:23 +03:00
use Lucent\Query\Filter\AndFilter;
use Lucent\Query\Filter\Argument;
use Lucent\Query\Filter\Filter;
use Lucent\Query\Filter\OrFilter;
2024-08-24 17:22:40 +03:00
use Lucent\Query\Operator\In;
use Lucent\Query\Operator\OperatorDetector;
use function explode;
2023-10-13 21:06:23 +03:00
final class FilterParser
{
2024-08-24 17:22:40 +03:00
public function __construct(
public Application $app,
public BuilderConverter $builderConverter,
public OperatorDetector $operatorDetector,
)
2023-10-13 21:06:23 +03:00
{
}
/**
* @param array $arguments
* @return array<Argument>
*/
private function formatArguments(array $arguments): array
{
return collect($arguments)->reduce(function ($c, $v, $k) {
2024-08-24 17:22:40 +03:00
$c[] = $this->operatorDetector->detect($v, $k);
2023-10-13 21:06:23 +03:00
return $c;
}, []);
}
private function formatReferences(array $referenceArguments): Argument
{
$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;
}, []);
2023-11-17 20:21:45 +02:00
2023-10-13 21:06:23 +03:00
$sourceIds = collect($subqueries)->reduce(function ($c, $subquery, $k) {
$query = $this->app->make(Query::class);
$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 new Argument(
field: "id",
2024-08-24 17:22:40 +03:00
operator: In::make(),
2023-10-13 21:06:23 +03:00
value: $sourceIds
);
}
private function separateMainFromReferenceArguments(Filter $arguments): array
{
return collect($arguments->toArray())->partition(function ($v, $k) {
if (!str_starts_with($k, "children.")) {
return true;
}
return false;
})->toArray();
}
/**
* @return array<Argument>
*/
private function parseArguments(Filter $arguments): array
{
[$normalArguments, $referenceArguments] = $this->separateMainFromReferenceArguments($arguments);
$formattedArguments = $this->formatArguments($normalArguments);
if (!empty($referenceArguments)) {
$formattedArguments[] = $this->formatReferences($referenceArguments);
}
return $formattedArguments;
}
public function parse(Builder $builder, Filter $filter): Builder
{
$arguments = $this->parseArguments($filter);
return match (get_class($filter)) {
AndFilter::class => $this->parseAnd($builder, $arguments),
OrFilter::class => $this->parseOr($builder, $arguments),
};
}
/**
* @param array<Argument> $arguments
*/
private function parseAnd(Builder $builder, array $arguments): Builder
{
2024-08-24 17:22:40 +03:00
return collect($arguments)
->reduce(fn($cBuilder, Argument $arg) => $this->builderConverter->for($arg)->toAndQueryBuilder($cBuilder), $builder);
2023-10-13 21:06:23 +03:00
}
/**
* @param array<Argument> $arguments
*/
private function parseOr(Builder $builder, array $arguments): Builder
{
2024-08-24 17:22:40 +03:00
return $builder->where(function (Builder $orBuilder) use ($arguments) {
return collect($arguments)
->reduce(fn($cBuilder, Argument $arg) => $this->builderConverter->for($arg)->toOrQueryBuilder($cBuilder), $orBuilder);
2023-10-13 21:06:23 +03:00
});
}
}