*/ private function formatArguments(array $arguments): array { return collect($arguments)->reduce(function ($c, $v, $k) { $c[] = $this->operatorDetector->detect($v, $k); 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; }, []); $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 = Database::make()->table("edges")->whereIn("target", $targetIds)->where("field", $k)->get()->pluck("source"); return array_merge($c, $sourceIds->toArray()); }, []); return new Argument( field: "id", operator: new In(), 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 */ 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 $arguments */ private function parseAnd(Builder $builder, array $arguments): Builder { return collect($arguments) ->reduce(fn($cBuilder, Argument $arg) => $this->builderConverter->for($arg)->toAndQueryBuilder($cBuilder), $builder); } /** * @param array $arguments */ private function parseOr(Builder $builder, array $arguments): Builder { return $builder->where(function (Builder $orBuilder) use ($arguments) { return collect($arguments) ->reduce(fn($cBuilder, Argument $arg) => $this->builderConverter->for($arg)->toOrQueryBuilder($cBuilder), $orBuilder); }); } }