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

258 lines
6.8 KiB
PHP
Raw Normal View History

2023-10-02 23:10:49 +03:00
<?php
namespace Lucent\Query;
use Illuminate\Database\Query\Builder;
use Illuminate\Support\Facades\DB;
use Lucent\Edge\Edge;
use Lucent\Primitive\Collection;
2023-10-15 23:40:34 +03:00
use Lucent\Query\DatabaseGraph\DatabaseGraph;
2023-10-13 21:06:23 +03:00
use Lucent\Query\Filter\AndFilter;
use Lucent\Query\Filter\OrFilter;
2023-10-02 23:10:49 +03:00
use Lucent\Record\InputFormatter;
use Lucent\Record\QueryRecord;
use Lucent\Record\Record;
final class Query
{
2023-10-13 21:06:23 +03:00
/**
* @var array<AndFilter> $filters
*/
public array $filters;
2023-10-02 23:10:49 +03:00
public QueryOptions $options;
public function __construct(
2023-10-13 21:06:23 +03:00
public readonly FilterParser $filterParser,
2023-10-02 23:10:49 +03:00
public readonly InputFormatter $inputFormatter,
2023-10-15 23:40:34 +03:00
public readonly DatabaseGraph $databaseGraph,
2023-10-02 23:10:49 +03:00
)
{
$this->options = new QueryOptions();
}
public function filter(array $filterArguments): Query
{
2023-10-13 21:06:23 +03:00
$this->filters[] = new AndFilter($filterArguments);
return $this;
}
public function orFilter(array $filterArguments): Query
{
$this->filters[] = new OrFilter($filterArguments);
2023-10-02 23:10:49 +03:00
return $this;
}
public function run(): Graph
{
$resultsRecords = $this->mainQuery();
$ids = array_map(function ($rec) {
return $rec->id;
}, $resultsRecords);
$resultChildrenEdgesTargetIds = [];
$resultChildrenEdges = [];
if ($this->options->childrenDepth > 0 && !empty($ids)) {
$resultChildrenEdges = $this->getChildren($ids);
$resultChildrenEdgesTargetIds = array_map(fn($e) => $e->target, $resultChildrenEdges);
}
$resultParentSourceTargetIds = [];
$resultParentEdges = [];
if ($this->options->parentsDepth > 0 && !empty($ids)) {
$resultParentEdges = $this->getParents($ids);
$resultParentSourceTargetIds = array_map(fn($e) => $e->source, $resultParentEdges);
}
$edgesIds = collect($resultParentSourceTargetIds)->merge($resultChildrenEdgesTargetIds)->unique()->values()->toArray();
$edgeRecords = [];
if (!empty($edgesIds)) {
$edgeRecords = DB::table('records')
->whereIn("id", $edgesIds)
2023-10-04 13:32:30 +03:00
->whereIn("status", $this->options->status)
2023-10-02 23:10:49 +03:00
->get()->toArray();
}
$resultsRecordsUnique = collect(array_merge($resultsRecords, $edgeRecords))->unique("id")->values()->toArray();
2023-10-13 21:06:23 +03:00
// $resultEdges = collect(array_merge($resultChildrenEdges, $resultParentEdges))
// ->unique(fn($edge) => $edge->source . $edge->target . $edge->field)
// ->toArray();
2023-10-02 23:10:49 +03:00
2023-10-13 21:06:23 +03:00
$this->reset();
return $this->formatRecords($resultsRecordsUnique, $resultChildrenEdges, $resultParentEdges);
2023-10-02 23:10:49 +03:00
2023-10-13 21:06:23 +03:00
}
2023-10-02 23:10:49 +03:00
2023-10-13 21:06:23 +03:00
private function reset()
{
$this->options = new QueryOptions();
$this->filters = [];
2023-10-02 23:10:49 +03:00
}
2023-10-13 21:06:23 +03:00
private function formatRecords(array $records, array $edges, array $parentEdges): Graph
2023-10-02 23:10:49 +03:00
{
$queryRecords = collect($records)->map(function ($recordData) {
$record = Record::fromDB($recordData);
2023-10-04 13:32:30 +03:00
$record->data = $this->inputFormatter->fill($record->schema, $record->data);
2023-10-02 23:10:49 +03:00
$queryRecord = QueryRecord::fromRecord($record);
$queryRecord->isRoot = data_get($recordData, "isRoot") === true;
return $queryRecord;
})->toArray();
$queryEdges = collect($edges)->map(function ($edgeData) {
return Edge::fromArray((array)$edgeData);
})->sortBy("rank")->values()->toArray();
2023-10-13 21:06:23 +03:00
$queryParentEdges = collect($parentEdges)->map(function ($edgeData) {
return Edge::fromArray((array)$edgeData);
})->sortBy("rank")->values()->toArray();
2023-10-02 23:10:49 +03:00
return new Graph(
new Collection($queryRecords),
new Collection($queryEdges),
2023-10-13 21:06:23 +03:00
new Collection($queryParentEdges),
2023-10-02 23:10:49 +03:00
);
}
public function tree(): Collection
{
return $this->run()->tree();
}
private function parseFilters(Builder $query): Builder
{
2023-10-13 21:06:23 +03:00
foreach ($this->filters as $filter) {
$query = $this->filterParser->parse($query, $filter);
2023-10-02 23:10:49 +03:00
}
2023-10-04 13:32:30 +03:00
$query->whereIn("status", $this->options->status);
2023-10-02 23:10:49 +03:00
return $query;
}
2023-10-13 21:06:23 +03:00
2023-10-02 23:10:49 +03:00
private function mainQuery(): array
{
$query = DB::table("records");
$query = $this->parseFilters($query);
2023-10-13 21:06:23 +03:00
if ($this->options->limit > 0) {
2023-10-08 02:12:02 +03:00
$query->limit($this->options->limit);
$query->offset($this->options->skip);
}
2023-10-02 23:10:49 +03:00
$query = $this->orderByQuery($query);
2023-10-23 19:43:59 +03:00
2023-10-02 23:10:49 +03:00
return $query->get()->map(function ($r) {
$r->isRoot = true;
return $r;
})->toArray();
}
private
function getChildren(array $ids): array
{
2023-10-15 23:40:34 +03:00
return $this->databaseGraph->getChildren($ids, $this->options);
2023-10-02 23:10:49 +03:00
}
2023-10-13 21:06:23 +03:00
private function getParents(array $ids): array
2023-10-02 23:10:49 +03:00
{
2023-10-15 23:40:34 +03:00
return $this->databaseGraph->getParents($ids, $this->options);
2023-10-02 23:10:49 +03:00
}
2023-10-04 13:32:30 +03:00
2023-10-02 23:10:49 +03:00
public
function runWithCount(): Graph
{
$query = DB::table("records");
$query = $this->parseFilters($query);
$graph = $this->run();
$graph->total = $query->count();
return $graph;
}
public
function limit(int $limit): Query
{
$this->options->limit = $limit;
return $this;
}
public
function skip(int $skip): Query
{
$this->options->skip = $skip;
return $this;
}
public function childrenDepth(int $depth): Query
{
$this->options->childrenDepth = $depth;
return $this;
}
public function childrenLimit(int $limit): Query
{
$this->options->childrenLimit = $limit;
return $this;
}
2023-10-13 21:06:23 +03:00
public function childrenFields(array $fields): Query
{
$this->options->childrenFields = $fields;
return $this;
}
public function parentFields(array $fields): Query
{
$this->options->parentFields = $fields;
return $this;
}
2023-10-02 23:10:49 +03:00
public function parentsDepth(int $depth): Query
{
$this->options->parentsDepth = $depth;
return $this;
}
public function parentsLimit(int $limit): Query
{
$this->options->parentsLimit = $limit;
return $this;
}
public function sort(string $sort): Query
{
$this->options->sort[] = $sort;
return $this;
}
public function status(array $status): Query
{
$this->options->status = $status;
return $this;
}
public
function orderByQuery(Builder $query): Builder
{
foreach ($this->options->sort as $item) {
$field = str_replace(".", "->", ltrim($item, '-'));
$dir = str_starts_with($item, '-') ? "desc" : "asc";
if ($field) {
$query->orderBy($field, $dir);
}
}
return $query;
}
}