query rewrite

This commit is contained in:
2024-03-30 13:42:38 +02:00
parent b4521e92b8
commit 0e19c38f23
91 changed files with 1051 additions and 852 deletions
+10 -10
View File
@@ -3,25 +3,25 @@
import {onDestroy, onMount} from "svelte";
import offcanvas from "bootstrap/js/src/offcanvas.js";
export let title = "";
let offcanvasEl;
let offcanvasInstance;
let offCanvasEl;
let offCanvasInstance;
export function show() {
offcanvasInstance.show();
if(!offCanvasInstance){
offCanvasInstance = new offcanvas(offCanvasEl);
}
offCanvasInstance.show();
}
onMount(()=>{
offcanvasInstance = new offcanvas(offcanvasEl);
offCanvasInstance = new offcanvas(offCanvasEl);
});
export function hide(e) {
e.preventDefault();
offcanvasInstance.hide();
export function hide() {
offCanvasInstance.hide();
}
</script>
<div bind:this={offcanvasEl} class="offcanvas offcanvas-end" tabindex="-1"
<div bind:this={offCanvasEl} class="offcanvas offcanvas-end" tabindex="-1"
aria-labelledby="offcanvasEditContent">
<div class="offcanvas-header">
<h5 class="offcanvas-title">{title}</h5>
+14 -14
View File
@@ -5,6 +5,7 @@
import Table from "./Table.svelte";
import {getContext} from "svelte";
import Grid from "./Grid.svelte";
import Tools from "./tools/Tools.svelte";
const axios = getContext("axios");
export let schema;
@@ -53,20 +54,19 @@
{#if selected.length > 0 && !inModal && isWritable}
<ActionsOnSelected {schema} {selected} {filter}/>
{:else}
<!-- <Tools-->
<!-- bind:schema-->
<!-- bind:records-->
<!-- {systemFields}-->
<!-- {sortParam}-->
<!-- {sortField}-->
<!-- {operators}-->
<!-- {filter}-->
<!-- {graph}-->
<!-- {inModal}-->
<!-- {modalUrl}-->
<!-- {isWritable}-->
<!-- on:refresh={refresh}-->
<!-- />-->
<Tools
bind:schema
bind:records
{systemFields}
{sortParam}
{sortField}
{operators}
{filter}
{inModal}
{modalUrl}
{isWritable}
on:refresh={refresh}
/>
{/if}
{#if schema.type === "collection"}
@@ -10,7 +10,7 @@
export let value;
export let inModal;
export let modalUrl;
export let graph;
export let records
let filter = {
label: "",
@@ -51,13 +51,13 @@
}
const filterRecord = extractFilterRecord(graph, value);
const filterRecord = extractFilterRecord(records, value);
function extractFilterRecord(graph, value) {
function extractFilterRecord(records, value) {
if (!filter.isReference) {
return null;
}
return graph.records.find(r => r.id === value);
return records.find(r => r.id === value);
}
function removeFilter(k) {
+2 -3
View File
@@ -18,7 +18,6 @@
export let modalUrl;
export let isWritable;
export let records;
export let graph;
export let systemFields = [];
// export let visibleFields = [];
@@ -37,7 +36,7 @@
if (inModal) {
dispatch("refresh", url);
} else {
window.location = url;
window.location.href = url;
}
}
@@ -152,7 +151,7 @@
value={v}
{inModal}
{modalUrl}
{graph}
{records}
on:refresh
/>
{/each}
+3
View File
@@ -15,6 +15,7 @@
// export let isWritable = false;
// export let users;
$: validationErrors = null;
$: errorMessage = null;
let form;
@@ -90,6 +91,8 @@
{schema}
{record}
{isCreateMode}
{errorMessage}
{validationErrors}
on:save={save}
/>
<!-- <Graph {graph} {record}/>-->
+1 -1
View File
@@ -20,7 +20,7 @@
}
</script>
<h3 class="header-normal mt-5 mb-0">
<h3 class="header-normal mb-0">
<a
class="text-muted d-block text-decoration-none fs-6 mb-1"
href="{channel.lucentUrl}/content/{schema.name}"
+2 -2
View File
@@ -11,7 +11,7 @@
const channel = getContext("channel");
export let record;
export let field;
export let edge;
export let edge = null;
export let editable = false;
export let classes = "";
export let hasDelete = false;
@@ -51,7 +51,7 @@
</div>
</div>
<EdgeData bind:this={edgeData} {field} {edge}/>
<EdgeData bind:this={edgeData} {record} {field} bind:edge/>
{/if}
<div
+1
View File
@@ -1,5 +1,6 @@
import { deepEqual } from 'fast-equals';
export function isEqual(obj1, obj2) {
return deepEqual(obj1, obj2);
// if (obj1 === obj2) return true;
//
+4 -10
View File
@@ -7,14 +7,12 @@
import ReferenceField from "./ReferenceField.svelte";
import SaveButtons from "./SaveButtons.svelte";
import EditHeader from "../EditHeader.svelte";
const dispatch = createEventDispatcher();
function save() {
dispatch("save", {
status: status
});
}
export let title = null;
@@ -27,19 +25,18 @@
let originalContent;
let activeContentTab = "";
$: hasUnsavedData = false;
$: validationErrors = null;
$: errorMessage = validationErrors
export let validationErrors = null;
export let errorMessage = validationErrors
? `Record submission failed. ${
Object.entries(validationErrors).length
} error(s)`
: null;
export function setOriginalData() {
originalContent = {
data: JSON.parse(JSON.stringify(data)),
status: status,
edges: JSON.parse(JSON.stringify(graph.map(r => r.edge))),
edges: JSON.parse(JSON.stringify(graph?.map(r => r.edge.target+r.edge.field) ?? [])),
};
hasUnsavedData = checkUnsavedData();
@@ -49,7 +46,6 @@
setOriginalData()
})
afterUpdate(() => {
hasUnsavedData = checkUnsavedData();
});
@@ -79,11 +75,9 @@
return !isEqual(originalContent, {
data: data,
status: status,
edges: graph.map(r => r.edge),
edges: graph?.map(r => r.edge.target+r.edge.field) ?? [],
});
}
</script>
<svelte:window on:beforeunload={beforeUnload}/>
@@ -3,13 +3,16 @@
import {getContext} from "svelte";
import Form from "../Form.svelte";
import OffCanvas from "../../../common/OffCanvas.svelte";
import Preview from "../../../newPreview/Preview.svelte";
import PreviewCard from "../../PreviewCard.svelte";
import axios from "axios";
export let field;
export let record;
export let edge;
let form;
let offCanvas;
$: validationErrors = null;
$: errorMessage = null;
const channel = getContext("channel");
let schema = channel.schemas.find(s => s.name === field.data);
@@ -17,20 +20,56 @@
offCanvas.show();
}
function save(e){
e.preventDefault();
console.log("yo")
console.log("SAVE: Attempt");
validationErrors = null;
return new Promise(function (resolve, reject) {
axios
.put(channel.lucentUrl + "/edges", edge)
.then(function (response) {
console.log("SAVE: SAVED");
edge = response.data;
form.setOriginalData();
resolve(null);
offCanvas.hide();
})
.catch(function (error) {
// setOriginalContent();
if (error.response) {
if (typeof error.response.data.error === "string") {
errorMessage = error.response.data.error;
} else {
validationErrors = error.response.data.error;
console.log(validationErrors)
}
}
resolve(null);
});
});
}
</script>
<OffCanvas bind:this={offCanvas}>
<div class="p-4">
<PreviewCard
{record}
hasDelete={false}
editable={false}
{field}
/>
</div>
<Form
bind:this={form}
data={edge.data}
title={"Relational Data for " + field.info.label}
{schema}
isCreateMode={false}
{errorMessage}
{validationErrors}
on:save={save}
/>
</OffCanvas>
@@ -88,7 +88,7 @@
hasDelete={true}
editable={!!field?.data}
{field}
edge={reference.edge}
bind:edge={reference.edge}
on:remove={removeReference}
/>
</div>
@@ -10,6 +10,7 @@ export function insertEdges(existingRecords, sourceRecord, targetRecords, fieldN
sourceSchema: sourceRecord.schema,
targetSchema: r.schema,
field: fieldName,
data: {},
rank: ""
}
+1 -1
View File
@@ -3,7 +3,7 @@
namespace Lucent\Commands;
use Illuminate\Console\Command;
use Lucent\Edge\EdgeService;
use Lucent\Graph\Edge\EdgeService;
use Lucent\Query\Query;
class RemoveOrphanEdges extends Command
+1
View File
@@ -21,6 +21,7 @@ return new class extends Migration {
$table->jsonb('_file');
$table->jsonb('_edges');
});
}
/**
@@ -0,0 +1,33 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration {
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::table('revisions', function (Blueprint $table) {
$table->dropColumn("schema");
$table->dropColumn("_file");
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::table('revisions', function (Blueprint $table) {
$table->string('schema');
$table->jsonb('_file');
});
}
};
-50
View File
@@ -1,50 +0,0 @@
<?php
namespace Lucent\Edge;
use Lucent\ArrayContainer;
class EdgeData extends ArrayContainer
{
public function merge(EdgeData $data): EdgeData
{
$this->data = array_merge($this->data, $data->toArray());
return $this;
}
public function get(string $key, mixed $default = null): mixed
{
return $this->data[$key] ?? $default;
}
public function set(string $key, mixed $value): EdgeData
{
$this->data[$key] = $value;
return $this;
}
public function isEmpty(string $key): bool
{
return empty($this->get($key));
}
public function isDefined(string $key): bool
{
return !empty($this->get($key));
}
public function toArray(): array
{
return $this->data;
}
public function jsonSerialize(): array
{
return $this->data;
}
}
-85
View File
@@ -1,85 +0,0 @@
<?php namespace Lucent\Edge;
use Illuminate\Support\Facades\DB;
use Lucent\LucentException;
use PDOException;
use PhpOption\Option;
use stdClass;
class EdgeRepo
{
public function __construct()
{
}
public function insert(Edge $edge): void
{
try {
DB::table("edges")->insert($edge->toDB());
} catch (PDOException $e) {
if ($e->getCode() == 23505) {
throw new LucentException("Edge already exists");
}
throw $e;
}
}
/**
* @param string $from
* @param EdgeCollection $edges
* @return void
*/
public function update(string $from, EdgeCollection $edges): void
{
DB::table("edges")->where("source", $from)->delete();
if($edges->isEmpty()){
return;
}
$edgesDB = collect($edges)->map(fn(Edge $e) => $e->toDB())->toArray();
DB::table("edges")->insert($edgesDB);
}
public function findAll(): EdgeCollection
{
$edges = DB::table("edges")->get();
return new EdgeCollection(...$edges->map([$this, 'mapEdge'])->toArray());
}
public function mapEdge(stdClass $edge): Edge
{
if (empty($edge->data)) {
$data = none();
} else {
$data = some(new EdgeData(json_decode($edge->data)));
}
return new Edge(
source: $edge->source,
target: $edge->target,
sourceSchema: $edge->sourceSchema,
targetSchema: $edge->targetSchema,
field: $edge->field,
data: $data,
rank: $edge->rank,
depth: $edge->depth ?? 0
);
}
public function remove(Edge $edge): void
{
DB::table("edges")
->where("source", $edge->source)
->where("target", $edge->target)
->where("sourceSchema", $edge->sourceSchema)
->where("targetSchema", $edge->targetSchema)
->where("field", $edge->field)
->delete();
}
}
-59
View File
@@ -1,59 +0,0 @@
<?php namespace Lucent\Edge;
use Lucent\LucentException;
use PhpOption\Option;
class EdgeService
{
public function __construct(public EdgeRepo $edgeRepo)
{
}
/**
* @throws LucentException
*/
public function create(
string $source,
string $target,
string $sourceSchema,
string $targetSchema,
string $field,
string $rank,
): Edge
{
$edge = new Edge(
source: $source,
target: $target,
sourceSchema: $sourceSchema,
targetSchema: $targetSchema,
field: $field,
rank: $rank,
);
$this->edgeRepo->insert($edge);
return $edge;
}
/**
* @param string $from
* @param EdgeCollection $edges
* @return void
*/
public function update(string $from, EdgeCollection $edges): void
{
$this->edgeRepo->update($from, $edges);
}
public function findAll(): EdgeCollection
{
return $this->edgeRepo->findAll();
}
public function remove(Edge $edge): void
{
$this->edgeRepo->remove($edge);
}
}
+2 -2
View File
@@ -4,9 +4,9 @@ namespace Lucent\File;
use Illuminate\Http\UploadedFile;
use Lucent\Channel\ChannelService;
use Lucent\Graph\Record\FileInfo;
use Lucent\Graph\Record\QueryRecord;
use Lucent\LucentException;
use Lucent\Record\FileInfo;
use Lucent\Record\QueryRecord;
use Lucent\Schema\Schema\Schema;
use Lucent\Schema\Schema\Type;
use Lucent\Support\Result\Result;
+1 -1
View File
@@ -2,7 +2,7 @@
namespace Lucent\File;
use Lucent\Record\FileInfo;
use Lucent\Graph\Record\FileInfo;
class FileUploadResult
{
+1 -1
View File
@@ -6,7 +6,7 @@ use Exception;
use Illuminate\Log\Logger;
use Intervention\Image\ImageManager;
use Lucent\Channel\ChannelService;
use Lucent\Record\QueryRecord;
use Lucent\Graph\Record\QueryRecord;
class ImageService
{
+1 -1
View File
@@ -9,8 +9,8 @@ use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Str;
use Intervention\Image\ImageManagerStatic;
use Lucent\Graph\Record\FileInfo as RecordFile;
use Lucent\LucentException;
use Lucent\Record\FileInfo as RecordFile;
use Lucent\Schema\Schema\Schema;
use Spatie\ImageOptimizer\OptimizerChainFactory;
@@ -1,14 +1,14 @@
<?php
namespace Lucent\Record;
namespace Lucent\Graph\Data;
use Lucent\ArrayContainer;
class RecordData extends ArrayContainer
class FieldData extends ArrayContainer
{
public function merge(RecordData $data): RecordData
public function merge(FieldData $data): FieldData
{
$this->data = array_merge($this->data, $data->toArray());
@@ -20,7 +20,7 @@ class RecordData extends ArrayContainer
return $this->data[$key] ?? $default;
}
public function set(string $key, mixed $value): RecordData
public function set(string $key, mixed $value): FieldData
{
$this->data[$key] = $value;
return $this;
@@ -47,5 +47,13 @@ class RecordData extends ArrayContainer
return $this->data;
}
public static function fromArray(array $data):self{
return new self($data);
}
public static function fromJson(string $data):self{
return self::fromArray(json_decode($data,true));
}
}
@@ -1,6 +1,6 @@
<?php
namespace Lucent\Record;
namespace Lucent\Graph\Data;
use Lucent\Channel\ChannelService;
use Lucent\Schema\Field\FieldDataInterface;
@@ -15,12 +15,13 @@ class InputFormatter
{
}
public function fill(string $schemaName, RecordData $input): RecordData
public function fill(string $schemaName, FieldData $input): FieldData
{
$schema = $this->channelService->getSchema($schemaName)->get();
return $schema->fields
->filter(fn(FieldInterface $field)=> $field instanceof FieldDataInterface)
->reduce(fn(RecordData $carry, FieldDataInterface $field) => $field->format($input, $carry), new RecordData([]));
->reduce(fn(FieldData $carry, FieldDataInterface $field) => $field->format($input, $carry), new FieldData([]));
}
+18
View File
@@ -0,0 +1,18 @@
<?php
namespace Lucent\Graph\Edge\Contracts;
class EdgeData
{
public function __construct(
public string $source,
public string $target,
public string $sourceSchema,
public string $targetSchema,
public string $field,
public array $data,
public string $rank,
)
{
}
}
+16 -19
View File
@@ -1,34 +1,30 @@
<?php
namespace Lucent\Edge;
namespace Lucent\Graph\Edge;
use Lucent\LucentException;
use Lucent\Validator\Validator as LucentValidator;
use PhpOption\Option;
use Lucent\Graph\Data\FieldData;
use stdClass;
final class Edge
{
/**
* @param Option<EdgeData> $data
*/
public function __construct(
public string $source,
public string $target,
public string $sourceSchema,
public string $targetSchema,
public string $field,
public Option $data,
public string $rank = "a",
public int $depth = 0,
public string $source,
public string $target,
public string $sourceSchema,
public string $targetSchema,
public string $field,
public FieldData $data,
public string $rank = "a",
public int $depth = 0,
)
{
}
public function equal(Edge $edge): bool
{
return $this->targetSchema === $edge->targetSchema && $this->field === $edge->field && $this->target === $edge->target && $this->source === $edge->source;
return $this->field === $edge->field && $this->target === $edge->target && $this->source === $edge->source;
}
public function toArray(): array
@@ -46,14 +42,13 @@ final class Edge
public static function fromArray(array $data): Edge
{
return new Edge(
source: data_get($data, 'source'),
target: data_get($data, 'target'),
sourceSchema: data_get($data, 'sourceSchema'),
targetSchema: data_get($data, 'targetSchema'),
field: data_get($data, 'field'),
data: Option::fromValue(data_get($data,"data")),
data: FieldData::fromArray(data_get($data, "data", [])),
rank: data_get($data, 'rank'),
depth: data_get($data, 'depth', 0),
);
@@ -69,9 +64,11 @@ final class Edge
sourceSchema: data_get($data, 'sourceSchema'),
targetSchema: data_get($data, 'targetSchema'),
field: data_get($data, 'field'),
data: Option::fromValue(data_get($data,"data")),
data: FieldData::fromJson($data->data ?? "{}"),
rank: data_get($data, 'rank'),
depth: data_get($data, 'depth', 0),
);
}
}
@@ -1,16 +1,16 @@
<?php
namespace Lucent\Edge;
namespace Lucent\Graph\Edge;
use Illuminate\Support\Collection;
/**
* @extends \Illuminate\Support\Collection<int|string, Edge>
* @extends Collection<int|string, Edge>
*/
final class EdgeCollection extends Collection
{
public function __construct(
private function __construct(
Edge ...$array
)
{
@@ -25,10 +25,24 @@ final class EdgeCollection extends Collection
return collect($this)->values()->toArray();
}
public function toDB(): array
{
return collect($this)->map(fn(Edge $edge) => $edge->toDB())->toArray();
}
/**
* @param array<mixed|Edge> $data
* @return EdgeCollection
*/
public static function fromArray(array $data): EdgeCollection
{
$edges = collect($data)
->map([Edge::class, 'fromArray'])
->map(function (mixed $edge) {
if ($edge instanceof Edge) {
return $edge;
}
return Edge::fromArray($edge);
})
->unique(fn(Edge $e) => $e->field . $e->source . $e->target);
return new EdgeCollection(...$edges);
}
+89
View File
@@ -0,0 +1,89 @@
<?php namespace Lucent\Graph\Edge;
use Illuminate\Support\Facades\DB;
use Lucent\Graph\Data\FieldData;
use Lucent\LucentException;
use PDOException;
use stdClass;
class EdgeRepo
{
public function __construct()
{
}
public function replaceBySourceId(string $sourceId, EdgeCollection $edgeCollection): void
{
DB::transaction(function () use ($sourceId, $edgeCollection) {
DB::table("edges")->where("source", $sourceId)->delete();
DB::table("edges")->insert($edgeCollection->toDB());
});
}
public function insert(Edge $edge): void
{
try {
DB::table("edges")->insert($edge->toDB());
} catch (PDOException $e) {
if ($e->getCode() == 23505) {
throw new LucentException("Edge already exists");
}
throw $e;
}
}
public function update(Edge $edge): void
{
DB::table("edges")
->where("source", $edge->source)
->where("target", $edge->target)
->where("field", $edge->field)
->update([
"data" => json_encode($edge->data),
"rank" => $edge->rank,
]);
}
// public function findAll(): EdgeCollection
// {
// $edges = DB::table("edges")->get();
// return new EdgeCollection(...$edges->map([$this, 'mapEdge'])->toArray());
// }
//
// public function mapEdge(stdClass $edge): Edge
// {
// if (empty($edge->data)) {
// $data = none();
// } else {
// $data = some(new FieldData(json_decode($edge->data)));
// }
//
//
// return new Edge(
// source: $edge->source,
// target: $edge->target,
// sourceSchema: $edge->sourceSchema,
// targetSchema: $edge->targetSchema,
// field: $edge->field,
// data: $data,
// rank: $edge->rank,
// depth: $edge->depth ?? 0
// );
//
// }
//
// public function remove(Edge $edge): void
// {
// DB::table("edges")
// ->where("source", $edge->source)
// ->where("target", $edge->target)
// ->where("sourceSchema", $edge->sourceSchema)
// ->where("targetSchema", $edge->targetSchema)
// ->where("field", $edge->field)
// ->delete();
// }
}
+95
View File
@@ -0,0 +1,95 @@
<?php namespace Lucent\Graph\Edge;
use Lucent\Channel\ChannelService;
use Lucent\Graph\Edge\Contracts\EdgeData;
use Lucent\Graph\Record\Contracts\RecordEdgeData;
use Lucent\Graph\Record\Record;
use Lucent\LucentException;
use Lucent\Query\Query;
use Lucent\Support\Collection;
use Lucent\Support\Result\Result;
use Lucent\Support\Result\Success;
class EdgeService
{
public function __construct(
public EdgeRepo $edgeRepo,
public Mapper $mapper,
)
{
}
/**
* @param Record $record
* @param Collection<RecordEdgeData> $edges
* @return EdgeCollection
*/
public function replaceEdgesForRecord(Record $record, Collection $edges): EdgeCollection
{
$edgeCollection = $this->getUniqueRecordEdges($edges, $record);
$this->edgeRepo->replaceBySourceId($record->id, $edgeCollection);
return $edgeCollection;
}
public function update(EdgeData $data) :Result{
$edge = $this->mapper->fromArray(toArray($data));
$this->edgeRepo->update($edge);
return Success::create($edge);
}
//
// /**
// * @throws LucentException
// */
// public function create(
// string $source,
// string $target,
// string $sourceSchema,
// string $targetSchema,
// string $field,
// string $rank,
// ): Edge
// {
//
//
// $edge = new Edge(
//
// source: $source,
// target: $target,
// sourceSchema: $sourceSchema,
// targetSchema: $targetSchema,
// field: $field,
// rank: $rank,
// );
// $this->edgeRepo->insert($edge);
// return $edge;
// }
//
// /**
// * @param string $from
// * @param EdgeCollection $edges
// * @return void
// */
// public function update(string $from, EdgeCollection $edges): void
// {
// $this->edgeRepo->update($from, $edges);
// }
//
// public function findAll(): EdgeCollection
// {
// return $this->edgeRepo->findAll();
// }
//
// public function remove(Edge $edge): void
// {
// $this->edgeRepo->remove($edge);
// }
private function getUniqueRecordEdges(Collection $edges, Record $record): EdgeCollection
{
$edges = $edges
->map(fn(RecordEdgeData $edge, $index) => $edge->toEdge($record, $index));
return EdgeCollection::fromArray($edges->toArray());
}
}
+45
View File
@@ -0,0 +1,45 @@
<?php
namespace Lucent\Graph\Edge;
use Lucent\Channel\ChannelService;
use Lucent\Graph\Data\InputFormatter;
use stdClass;
class Mapper
{
public function __construct(
public InputFormatter $inputFormatter,
public ChannelService $channelService
)
{
}
public function fromDB(stdClass $data): Edge
{
$edge = Edge::fromDB($data);
$edgeSchema = $this->getEdgeDataSchema($edge);
if(!empty($edgeSchema)){
$edge->data = $this->inputFormatter->fill($edgeSchema, $edge->data);
}
return $edge;
}
public function fromArray(array $data): Edge
{
$edge = Edge::fromArray($data);
$edgeSchema = $this->getEdgeDataSchema($edge);
if(!empty($edgeSchema)){
$edge->data = $this->inputFormatter->fill($edgeSchema, $edge->data);
}
return $edge;
}
private function getEdgeDataSchema(Edge $edge): string
{
return $this->channelService->getSchema($edge->sourceSchema)->get()->fields->where("name", $edge->field)->first()->data;
}
}
@@ -1,8 +1,8 @@
<?php
namespace Lucent\Record\Contracts;
namespace Lucent\Graph\Record\Contracts;
use Lucent\Record\QueryRecord;
use Lucent\Graph\Record\QueryRecord;
use Lucent\Support\Collection;
class EditorTree
@@ -1,8 +1,8 @@
<?php
namespace Lucent\Record\Contracts;
namespace Lucent\Graph\Record\Contracts;
use Lucent\Record\Status;
use Lucent\Graph\Record\Status;
use Lucent\Support\Collection;
use PhpOption\Option;
@@ -1,10 +1,11 @@
<?php
namespace Lucent\Record\Contracts;
namespace Lucent\Graph\Record\Contracts;
use Lucent\Edge\Edge;
use Lucent\Record\Record;
use PhpOption\Option;
use Lucent\Graph\Data\FieldData;
use Lucent\Graph\Edge\Edge;
use Lucent\Graph\Record\Record;
use Lucent\Support\Option\Option;
class RecordEdgeData
{
@@ -26,7 +27,7 @@ class RecordEdgeData
sourceSchema: $record->schema,
targetSchema: $this->targetSchema,
field: $this->field,
data: $this->data,
data: $this->data->map([FieldData::class,'fromArray'])->getOrElse(new FieldData([])),
rank: $index,
);
}
@@ -1,8 +1,8 @@
<?php
namespace Lucent\Record\Contracts;
namespace Lucent\Graph\Record\Contracts;
use Lucent\Record\Status;
use Lucent\Graph\Record\Status;
use Lucent\Support\Collection;
class UpdateRecordData
@@ -1,19 +1,20 @@
<?php
namespace Lucent\Record;
namespace Lucent\Graph\Record;
use Illuminate\Support\Str;
use Lucent\Graph\Data\FieldData;
use stdClass;
class Document implements Record
{
function __construct(
public string $id,
public string $schema,
public Status $status,
public System $_sys,
public RecordData $data,
public string $id,
public string $schema,
public Status $status,
public System $_sys,
public FieldData $data,
)
{
}
@@ -53,7 +54,7 @@ class Document implements Record
schema: $data->schema,
status: Status::from($data->status),
_sys: System::fromArray(json_decode($data->_sys, true)),
data: new RecordData(json_decode($data->data, true)),
data: new FieldData(json_decode($data->data, true)),
);
}
}
@@ -1,19 +1,20 @@
<?php
namespace Lucent\Record;
namespace Lucent\Graph\Record;
use Illuminate\Support\Str;
use Lucent\Graph\Data\FieldData;
use stdClass;
class File implements Record
{
function __construct(
public string $id,
public string $schema,
public Status $status,
public System $_sys,
public RecordData $data,
public string $id,
public string $schema,
public Status $status,
public System $_sys,
public FieldData $data,
public FileInfo $_file,
)
{
@@ -54,7 +55,7 @@ class File implements Record
schema: $data->schema,
status: Status::from($data->status),
_sys: System::fromArray(json_decode($data->_sys, true)),
data: new RecordData(json_decode($data->data, true)),
data: new FieldData(json_decode($data->data, true)),
_file: FileInfo::fromJSON($data->_file),
);
}
@@ -1,6 +1,6 @@
<?php
namespace Lucent\Record;
namespace Lucent\Graph\Record;
readonly class FileInfo
@@ -1,6 +1,6 @@
<?php
namespace Lucent\Record;
namespace Lucent\Graph\Record;
use Illuminate\Contracts\Session\Session;
use Lucent\Query\Query;
@@ -81,13 +81,11 @@ class Manager
public function getRecords(?string $ignoreId = null): array
{
$graph = $this->query
->filter(["id_in" => $this->getIdsExcept($ignoreId)])
->limit(7)
->run();
$graph = $this->query->run(
fn($builder) => $builder->filter(["id_in" => $this->getIdsExcept($ignoreId)])
->limit(7)
);
return $this->order($graph->records->toArray());
}
}
@@ -1,7 +1,8 @@
<?php
namespace Lucent\Record;
namespace Lucent\Graph\Record;
use Lucent\Graph\Data\InputFormatter;
use stdClass;
class Mapper
@@ -1,8 +1,8 @@
<?php
namespace Lucent\Record;
namespace Lucent\Graph\Record;
use Lucent\Edge\Edge;
use Lucent\Graph\Edge\Edge;
use Lucent\Support\Collection;
use Lucent\Support\Option\Option;
@@ -1,9 +1,7 @@
<?php
namespace Lucent\Record;
namespace Lucent\Graph\Record;
use Illuminate\Support\Str;
use JsonSerializable;
use stdClass;
interface Record
@@ -1,6 +1,6 @@
<?php
namespace Lucent\Record;
namespace Lucent\Graph\Record;
use Illuminate\Support\Facades\DB;
@@ -10,7 +10,7 @@ class RecordRepo
{
}
public static function create(Record $record): void
public function create(Record $record): void
{
$recordToDB = $record->toDB();
@@ -21,14 +21,14 @@ class RecordRepo
/**
* @param array<string> $ids
*/
public static function updateStatusBulk(Status $status, array $ids): void
public function updateStatusBulk(Status $status, array $ids): void
{
DB::table("records")->whereIn("id", $ids)->update([
'status' => $status->value
]);
}
public static function update(Record $record): void
public function update(Record $record): void
{
$recordToDB = $record->toDB();
@@ -1,20 +1,21 @@
<?php
namespace Lucent\Record;
namespace Lucent\Graph\Record;
use Illuminate\Support\Str;
use Lucent\Account\AuthService;
use Lucent\Channel\ChannelService;
use Lucent\CommonData\Id;
use Lucent\Edge\Edge;
use Lucent\Edge\EdgeCollection;
use Lucent\Edge\EdgeService;
use Lucent\File\FileService;
use Lucent\Graph\Data\FieldData;
use Lucent\Graph\Data\InputFormatter;
use Lucent\Graph\Edge\Edge;
use Lucent\Graph\Edge\EdgeCollection;
use Lucent\Graph\Edge\EdgeService;
use Lucent\Graph\Record\Contracts\NewDocumentData;
use Lucent\Graph\Record\Contracts\UpdateRecordData;
use Lucent\LucentException;
use Lucent\Query\Query;
use Lucent\Record\Contracts\NewDocumentData;
use Lucent\Record\Contracts\RecordEdgeData;
use Lucent\Record\Contracts\UpdateRecordData;
use Lucent\Revision\RevisionService;
use Lucent\Schema\Field\FieldDataInterface;
use Lucent\Schema\Schema\Schema;
@@ -25,7 +26,6 @@ use Lucent\Support\Collection;
use Lucent\Support\Result\Error;
use Lucent\Support\Result\Result;
use Lucent\Support\Result\Success;
use PhpOption\Option;
readonly class RecordService
{
@@ -50,7 +50,7 @@ readonly class RecordService
public function createDocument(NewDocumentData $data): Result
{
$schema = $this->channelService->getSchema($data->schemaName)->get();
$formattedData = $this->inputFormatter->fill($data->schemaName, new RecordData($data->data));
$formattedData = $this->inputFormatter->fill($data->schemaName, new FieldData($data->data));
$newRecordId = $data->id->getOrElse(Id::new());
$record = new Document(
@@ -61,32 +61,58 @@ readonly class RecordService
data: $formattedData,
);
$uniqueEdges = $this->getUniqueEdges($data->edges, $record);
if ($data->status === Status::PUBLISHED) {
$errors = $this->recordValidator->check($data->schemaName, $record->data);
if ($errors->isNotEmpty()) {
return Error::create($errors);
}
}
RecordRepo::create($record);
$this->edgeService->update($record->id, $uniqueEdges);
$this->revisionService->create($record, $uniqueEdges);
$this->recordRepo->create($record);
$edgeCollection = $this->edgeService->replaceEdgesForRecord($record,$data->edges);
$this->revisionService->create($record, $edgeCollection);
return Success::create($record->id);
}
/**
* @param Collection<RecordEdgeData> $edges
* @param Record $record
* @return EdgeCollection
* @param UpdateRecordData $data
* @return Result<string|Collection<ValidatorError>>
*/
private function getUniqueEdges(Collection $edges, Record $record): EdgeCollection
public function update(UpdateRecordData $data): Result
{
$edges = $edges
->map(fn(RecordEdgeData $edge, $index) => $edge->toEdge($record, $index));
return new EdgeCollection(...$edges->toArray());
$record = $this->query->filter(["id" => $data->id])->run()->rootRecords->first();
if (empty($record)) {
return Error::create("Record id is missing");
}
$formattedData = $this->inputFormatter->fill($record->schema, new FieldData($data->data));
if ($data->status === Status::PUBLISHED) {
$errors = $this->recordValidator->check($record->schema, $record->data);
if ($errors->isNotEmpty()) {
return Error::create($errors);
}
}
$newRecord = new Document(
id: $record->id,
schema: $record->schema,
status: $data->status,
_sys: $record->_sys->update($this->authService->currentUserId()),
data: $record->data->merge($formattedData),
);
$this->recordRepo->update($newRecord);
$edgeCollection = EdgeCollection::fromArray([]);
if ($data->updateEdges) {
$edgeCollection = $this->edgeService->replaceEdgesForRecord($record,$data->edges);
}
$this->revisionService->create($newRecord, $edgeCollection);
return Success::create($record->id);
}
/**
* @throws LucentException
* @throws ValidatorException
@@ -104,7 +130,7 @@ readonly class RecordService
{
$schema = $this->channelService->getSchema($schemaName)->get();
$formattedData = $this->inputFormatter->fill($schemaName, new RecordData($data));
$formattedData = $this->inputFormatter->fill($schemaName, new FieldData($data));
if (empty($formattedData["id"])) {
$formattedData["id"] = Id::new();
}
@@ -151,45 +177,7 @@ readonly class RecordService
}
/**
* @param UpdateRecordData $data
* @return Result<string|Collection<ValidatorError>>
*/
public function update(UpdateRecordData $data): Result
{
$record = $this->query->filter(["id" => $data->id])->run()->rootRecords->first();
if (empty($record)) {
return Error::create("Record id is missing");
}
$formattedData = $this->inputFormatter->fill($record->schema, new RecordData($data->data));
$uniqueEdges = new EdgeCollection();
if ($data->updateEdges) {
$uniqueEdges = $this->getUniqueEdges($data->edges, $record);
}
if ($data->status === Status::PUBLISHED) {
$errors = $this->recordValidator->check($record->schema, $record->data);
if ($errors->isNotEmpty()) {
return Error::create($errors);
}
}
$newRecord = new Document(
id: $record->id,
schema: $record->schema,
status: $data->status,
_sys: $record->_sys->update($this->authService->currentUserId()),
data: $record->data->merge($formattedData),
);
RecordRepo::update($newRecord);
if ($data->updateEdges) {
$this->edgeService->update($newRecord->id, $uniqueEdges);
}
$this->revisionService->create($newRecord, $uniqueEdges);
return Success::create($record->id);
}
/**
*/
@@ -290,7 +278,7 @@ readonly class RecordService
return $carry;
}, []);
$formattedData = $this->inputFormatter->fill($schema->name, new RecordData($defaultValues));
$formattedData = $this->inputFormatter->fill($schema->name, new FieldData($defaultValues));
return new Document(
id: Id::new(),
schema: $schema->name,
@@ -1,6 +1,6 @@
<?php
namespace Lucent\Record;
namespace Lucent\Graph\Record;
enum Status: string
{
@@ -1,6 +1,6 @@
<?php
namespace Lucent\Record;
namespace Lucent\Graph\Record;
use Carbon\Carbon;
@@ -3,12 +3,6 @@
namespace Lucent\Http\Controller\Api;
use Lucent\Http\Controller;
use Illuminate\Http\Request;
use Illuminate\Http\Response;
use Lucent\Edge\EdgeService;
use Lucent\LucentException;
use function Lucent\Response\fail;
use function Lucent\Response\ok;
class EdgeController extends Controller
{
@@ -3,14 +3,6 @@
namespace Lucent\Http\Controller\Api;
use Lucent\Http\Controller;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Validator;
use Lucent\Account\AuthService;
use Lucent\File\FileUploadResult;
use Lucent\Record\RecordService;
use function Lucent\File\uploadFile;
use function Lucent\Response\fail;
use function Lucent\Response\ok;
class FileController extends Controller
{
+1 -9
View File
@@ -2,16 +2,8 @@
namespace Lucent\Http\Controller\Api;
use Lucent\Http\Controller;
use Illuminate\Http\Request;
use Lucent\Channel\ChannelRepo;
use Lucent\LucentException;
use Lucent\Query\Query;
use Lucent\Record\RecordService;
use Lucent\Schema\Validator\ValidatorException;
use Throwable;
use function Lucent\Response\fail;
use function Lucent\Response\ok;
use Lucent\Http\Controller;
class RecordController extends Controller
{
+35
View File
@@ -0,0 +1,35 @@
<?php
namespace Lucent\Http\Controller;
use Illuminate\Http\Request;
use Lucent\Graph\Edge\Contracts\EdgeData;
use Lucent\Graph\Edge\EdgeService;
use Lucent\Http\Controller;
use function Lucent\Response\result;
class EdgeController extends Controller
{
public function __construct(
private readonly EdgeService $edgeService,
)
{
}
public function update(Request $request)
{
$res = $this->edgeService->update(new EdgeData(
source: $request->input("source"),
target: $request->input("target"),
sourceSchema: $request->input("sourceSchema"),
targetSchema: $request->input("targetSchema"),
field: $request->input("field"),
data: $request->input("data"),
rank: $request->input("rank"),
));
return result($res);
}
}
+2 -2
View File
@@ -2,13 +2,13 @@
namespace Lucent\Http\Controller;
use Lucent\Http\Controller;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Validator;
use Lucent\Channel\ChannelService;
use Lucent\File\FileUploadResult;
use Lucent\Graph\Record\RecordService;
use Lucent\Http\Controller;
use Lucent\Query\Query;
use Lucent\Record\RecordService;
use function Lucent\File\loadDisk;
use function Lucent\File\uploadFile;
use function Lucent\Response\fail;
+23 -24
View File
@@ -2,27 +2,28 @@
namespace Lucent\Http\Controller;
use Lucent\Http\Controller;
use Illuminate\Http\Request;
use Lucent\Account\AccountService;
use Lucent\Channel\ChannelService;
use Lucent\Graph\Record\Contracts\EditorTree;
use Lucent\Graph\Record\Contracts\NewDocumentData;
use Lucent\Graph\Record\Contracts\RecordEdgeData;
use Lucent\Graph\Record\Contracts\UpdateRecordData;
use Lucent\Graph\Record\Manager;
use Lucent\Graph\Record\QueryRecord;
use Lucent\Graph\Record\RecordService;
use Lucent\Graph\Record\Status;
use Lucent\Http\Controller;
use Lucent\LucentException;
use Lucent\Query\Builder;
use Lucent\Query\Operator;
use Lucent\Query\Query;
use Lucent\Record\Contracts\EditorTree;
use Lucent\Record\Contracts\NewDocumentData;
use Lucent\Record\Contracts\RecordEdgeData;
use Lucent\Record\Contracts\UpdateRecordData;
use Lucent\Record\Manager;
use Lucent\Record\QueryRecord;
use Lucent\Record\RecordService;
use Lucent\Record\Status;
use Lucent\Schema\Schema\System;
use Lucent\Schema\Validator\ValidatorException;
use Lucent\Support\Collection;
use Lucent\Support\Result\Success;
use Lucent\Svelte\Svelte;
use PhpOption\Option;
use Lucent\Support\Option\Option;
use function Lucent\Response\fail;
use function Lucent\Response\ok;
use function Lucent\Response\result;
@@ -36,7 +37,7 @@ class RecordController extends Controller
private readonly Svelte $svelte,
private readonly Query $query,
private readonly Manager $recordManager,
private readonly EditorTree $editorTree
private readonly EditorTree $editorTree
)
{
}
@@ -64,19 +65,17 @@ class RecordController extends Controller
"status_in" => "draft,published",
], $filter);
$skip = data_get($urlParams, "skip") ?? 0;
$limit = 30;
$graph = $this->query
->filter($arguments)
$graph = $this->query->run(fn(Builder $builder) => $builder->filter($arguments)
->limit($limit)
->status(explode(",", $arguments["status_in"]))
->skip($skip)
->sort($sort)
->childrenFields($schema?->visible ?? [])
->childrenDepth(1)
->parentsDepth(0)
->runWithCount();
->withCount()
);
$data = [
"schemas" => $this->channelService->channel->schemas,
@@ -102,6 +101,7 @@ class RecordController extends Controller
}
return $data;
}
$data["inModal"] = false;
return $this->svelte->render(
layout: "channel",
@@ -204,15 +204,14 @@ class RecordController extends Controller
{
$rid = $request->route("rid");
$graph = $this->query
->filter(["id" => $rid])
$graph = $this->query->run(fn(Builder $builder) => $builder->filter(["id" => $rid])
->limit(1)
->skip(0)
->childrenDepth(2)
->childrenLimit(200)
->childrenDepth(1)
->childrenLimit(500)
->parentsDepth(1)
->parentsLimit(200)
->run();
);
if ($graph->rootRecords->isEmpty()) {
return $this->svelte->render(
@@ -316,9 +315,9 @@ class RecordController extends Controller
$recordEdgeData = (new Collection($request->input("edges")))->map(fn($item) => new RecordEdgeData(
target: $item["target"],
targetSchema:$item["targetSchema"],
targetSchema: $item["targetSchema"],
field: $item["field"],
data: Option::fromValue(data_get($item,"data")),
data: Option::fromValue(data_get($item, "data")),
));
$res = match ($request->input("isCreateMode")) {
+6 -1
View File
@@ -4,6 +4,7 @@ use Illuminate\Support\Facades\Route;
use Lucent\Http\Controller\AccountController;
use Lucent\Http\Controller\AuthController;
use Lucent\Http\Controller\BuildController;
use Lucent\Http\Controller\EdgeController;
use Lucent\Http\Controller\FileController;
use Lucent\Http\Controller\HomeController;
use Lucent\Http\Controller\MemberController;
@@ -60,6 +61,11 @@ Route::group([
Route::post('/{rid}/rollback/{version}', [RecordController::class, 'rollback']);
});
Route::middleware(["lucent.auth"])->prefix("/edges")->group(function () {
Route::put('/', [EdgeController::class, 'update']);
});
Route::middleware(["lucent.auth"])->group(function () {
Route::get('/records/{rid}/revisions', [RevisionController::class, 'index']);
@@ -82,6 +88,5 @@ Route::group([
});
});
-1
View File
@@ -10,7 +10,6 @@ use function ord;
use function strcmp;
use function substr;
/** @psalm-immutable */
final class Lexorank
{
+9 -6
View File
@@ -16,6 +16,7 @@ use Lucent\JsonSchema\Command\GenerateJsonSchema;
use Lucent\Query\DatabaseGraph\DatabaseGraph;
use Lucent\Query\DatabaseGraph\PgsqlDatabaseGraph;
use Lucent\Query\DatabaseGraph\SqliteDatabaseGraph;
use Lucent\Query\Query;
use Lucent\Schema\Commands\CompileSchemas;
class LucentServiceProvider extends ServiceProvider
@@ -33,6 +34,8 @@ class LucentServiceProvider extends ServiceProvider
return new ImageManager(['driver' => 'imagick']);
});
$this->app->bind(DatabaseGraph::class, function () {
return match (config("lucent.database")) {
"sqlite" => new SqliteDatabaseGraph(),
@@ -61,17 +64,17 @@ class LucentServiceProvider extends ServiceProvider
$this->loadViewsFrom(__DIR__ . '/Views', 'lucent');
$this->loadRoutesFrom(__DIR__ . '/Http/web.php');
$this->loadRoutesFrom(__DIR__ . '/Http/api.php');
// $this->loadRoutesFrom(__DIR__ . '/Http/api.php');
$this->loadMigrationsFrom(__DIR__ . '/Database/migrations');
if ($this->app->runningInConsole()) {
$this->commands([
CompileSchemas::class,
RebuildThumbnails::class,
LiveLink::class,
RemoveOrphanEdges::class,
GenerateJsonSchema::class,
// CompileSchemas::class,
// RebuildThumbnails::class,
// LiveLink::class,
// RemoveOrphanEdges::class,
// GenerateJsonSchema::class,
]);
}
+237
View File
@@ -0,0 +1,237 @@
<?php
namespace Lucent\Query;
use Illuminate\Database\Query\Builder as LaravelBuilder;
use Lucent\Query\Data\AndFilter;
use Lucent\Query\Data\Argument;
use Lucent\Query\Data\Filter;
use Lucent\Query\Data\FilterGroup;
use Lucent\Query\Data\OrFilter;
class Builder
{
/**
* @param array<Filter> $filters
*/
public function __construct(
public array $filters = [],
public int $limit = 20,
public int $skip = 0,
public int $childrenDepth = -1,
public int $parentsDepth = -1,
public int $childrenLimit = -1,
public int $parentsLimit = -1,
public array $childrenFields = [],
public array $parentFields = [],
public array $sort = [],
public array $status = ["published", "draft"],
public bool $withCount = false
)
{
}
public function filter(array $filterArguments): Builder
{
$this->filters[] = new AndFilter($filterArguments);
return $this;
}
public function orFilter(array $filterArguments): Builder
{
$this->filters[] = new OrFilter($filterArguments);
return $this;
}
public function limit(int $limit): Builder
{
$this->limit = $limit;
return $this;
}
public function skip(int $skip): Builder
{
$this->skip = $skip;
return $this;
}
public function childrenDepth(int $depth): Builder
{
$this->childrenDepth = $depth;
return $this;
}
public function childrenLimit(int $limit): Builder
{
$this->childrenLimit = $limit;
return $this;
}
public function childrenFields(array $fields): Builder
{
$this->childrenFields = $fields;
return $this;
}
public function parentFields(array $fields): Builder
{
$this->parentFields = $fields;
return $this;
}
public function parentsDepth(int $depth): Builder
{
$this->parentsDepth = $depth;
return $this;
}
public function parentsLimit(int $limit): Builder
{
$this->parentsLimit = $limit;
return $this;
}
public function sort(string $sort): Builder
{
$this->sort[] = $sort;
return $this;
}
public function status(array $status): Builder
{
$this->status = $status;
return $this;
}
public function withCount(): Builder
{
$this->withCount = true;
return $this;
}
public function resolveRecordFilters(LaravelBuilder $laravelBuilder): LaravelBuilder
{
foreach ($this->filters as $filter) {
$groupFilters = $this->groupFilters($filter);
$filterParser = new FilterParser();
$arguments = $filterParser->formatArguments($groupFilters->record);
$laravelBuilder = match (get_class($filter)) {
AndFilter::class => $this->parseAnd($laravelBuilder, $arguments),
OrFilter::class => $this->parseOr($laravelBuilder, $arguments),
};
}
return $laravelBuilder;
}
public function resolveReferenceFilters(LaravelBuilder $laravelBuilder): LaravelBuilder
{
foreach ($this->filters as $filter) {
$groupFilters = $this->groupFilters($filter);
$filterParser = new FilterParser();
if(empty($groupFilters->reference)){
break;
}
$arguments[] = $filterParser->formatReferencesArguments($groupFilters->reference);
$laravelBuilder = match (get_class($filter)) {
AndFilter::class => $this->parseAnd($laravelBuilder, $arguments),
OrFilter::class => $this->parseOr($laravelBuilder, $arguments),
};
}
return $laravelBuilder;
}
public function resolveEdgeFilters(LaravelBuilder $laravelBuilder): LaravelBuilder
{
foreach ($this->filters as $filter) {
$groupFilters = $this->groupFilters($filter);
if(empty($groupFilters->edge)){
break;
}
$filterParser = new FilterParser();
$arguments[] = $filterParser->formatEdgesArguments($groupFilters->edge);
$laravelBuilder = match (get_class($filter)) {
AndFilter::class => $this->parseAnd($laravelBuilder, $arguments),
OrFilter::class => $this->parseOr($laravelBuilder, $arguments),
};
}
return $laravelBuilder;
}
private function groupFilters(Filter $arguments): FilterGroup
{
return collect($arguments->toArray())->reduce(function ($c, $v, $k) {
if (str_starts_with($k, "children.")) {
$c->reference[$k] = $v;
return $c;
}
if (str_starts_with($k, "edges.")) {
$c->edge[$k] = $v;
return $c;
}
$c->record[$k] = $v;
return $c;
}, new FilterGroup());
}
/**
* @param array<Argument> $arguments
*/
private function parseAnd(LaravelBuilder $builder, array $arguments): LaravelBuilder
{
foreach ($arguments as $argument) {
if ($argument->operator == "in") {
$builder->whereIn($argument->field, $argument->value);
} else if ($argument->operator == "nin") {
$builder->whereNotIn($argument->field, $argument->value);
} else if ($argument->operator == "exists") {
$builder->where($argument->field, "!=", "");
$builder->where($argument->field, "!=", null);
} elseif ($argument->operator == "filter") {
$builder->whereJsonContains($argument->field, [$argument->value]);
// target result
// filter[data.previousNames_object]=previousNames&filter[previousNames.name_eq]=alpha&filter[previousNames.id_eqnum]=24
// $query->whereJsonContains("data->previousNames", [["name" => "alpha", "id" => 24]]);
// $query->whereJsonContains($filter["field"], [$objectFilters]);
} else {
$builder->where($argument->field, $argument->operator, $argument->value);
}
}
return $builder;
}
/**
* @param array<Argument> $arguments
*/
private function parseOr(LaravelBuilder $builder, array $arguments): LaravelBuilder
{
$builder->where(function (LaravelBuilder $orBuilder) use ($arguments) {
foreach ($arguments as $argument) {
if ($argument->operator == "in") {
$orBuilder->orWhereIn($argument->field, $argument->value);
} else if ($argument->operator == "nin") {
$orBuilder->orWhereNotIn($argument->field, $argument->value);
} else if ($argument->operator == "exists") {
$orBuilder->where($argument->field, "!=", "");
$orBuilder->where($argument->field, "!=", null);
} elseif ($argument->operator == "filter") {
$orBuilder->whereJsonContains($argument->field, [$argument->value]);
// filter[data.previousNames_object]=previousNames&filter[previousNames.name_eq]=alpha&filter[previousNames.id_eqnum]=24
// $query->whereJsonContains("data->previousNames", [["name" => "alpha", "id" => 24]]);
// $query->whereJsonContains($filter["field"], [$objectFilters]);
} else {
$orBuilder->orWhere($argument->field, $argument->operator, $argument->value);
}
}
});
return $builder;
}
}
@@ -1,6 +1,6 @@
<?php
namespace Lucent\Query\Filter;
namespace Lucent\Query\Data;
final class AndFilter implements Filter
@@ -1,6 +1,6 @@
<?php
namespace Lucent\Query\Filter;
namespace Lucent\Query\Data;
class Argument
{
@@ -1,4 +1,4 @@
<?php namespace Lucent\Query\Filter;
<?php namespace Lucent\Query\Data;
interface Filter
+14
View File
@@ -0,0 +1,14 @@
<?php
namespace Lucent\Query\Data;
class FilterGroup
{
public function __construct(
public array $record = [],
public array $reference = [],
public array $edge = [],
)
{
}
}
@@ -1,6 +1,6 @@
<?php
namespace Lucent\Query\Filter;
namespace Lucent\Query\Data;
final class OrFilter implements Filter
+6 -5
View File
@@ -2,21 +2,22 @@
namespace Lucent\Query\DatabaseGraph;
use Lucent\Edge\Edge;
use Lucent\Query\Builder;
use Lucent\Query\QueryOptions;
use Lucent\Support\Collection;
use stdClass;
interface DatabaseGraph
{
/**
* @param array<string> $ids
* @return Collection<Edge>
* @return Collection<stdClass>
*/
public function getChildren(array $ids, QueryOptions $options): Collection;
public function getChildren(array $ids, Builder $options): Collection;
/**
* @param array<string> $ids
* @return Collection<Edge>
* @return Collection<stdClass>
*/
public function getParents(array $ids, QueryOptions $options): Collection;
public function getParents(array $ids, Builder $options): Collection;
}
+16 -13
View File
@@ -3,23 +3,24 @@
namespace Lucent\Query\DatabaseGraph;
use Illuminate\Support\Facades\DB;
use Lucent\Edge\Edge;
use Lucent\Query\QueryOptions;
use Lucent\Query\Builder;
use Lucent\Support\Collection;
use stdClass;
class PgsqlDatabaseGraph implements DatabaseGraph
{
/**
* @param array<string> $ids
* @return Collection<Edge>
* @return Collection<stdClass>
*/
public function getChildren(array $ids, QueryOptions $options): Collection
public function getChildren(array $ids, Builder $options): Collection
{
$subquery = DB::table('edges AS g')
->select(DB::raw('g.source,g.target,g.rank,"g"."sourceSchema","g"."targetSchema",g.field, 1 as depth '))
->select(DB::raw('g.source,g.target,g.rank,"g"."sourceSchema","g"."targetSchema",g.field,g.data, 1 as depth '))
->whereIn('source', $ids);
$subquery = $options->resolveEdgeFilters($subquery);
if (!empty($options->childrenFields)) {
$subquery->whereIn('field', $options->childrenFields);
}
@@ -27,7 +28,7 @@ class PgsqlDatabaseGraph implements DatabaseGraph
$subquery->limit($options->childrenLimit)
->union(
DB::table(DB::raw("edges AS g, search_graph AS sg "))
->selectRaw('g.source,g.target,g.rank,"g"."sourceSchema","g"."targetSchema",g.field,sg.depth + 1 as depth')
->selectRaw('g.source,g.target,g.rank,"g"."sourceSchema","g"."targetSchema",g.field,g.data,sg.depth + 1 as depth')
->whereRaw("g.source = sg.target")
->where("depth", "<", $options->childrenDepth)
->orderBy("rank")
@@ -36,19 +37,21 @@ class PgsqlDatabaseGraph implements DatabaseGraph
return new Collection(DB::table('search_graph')
// ->select(DB::raw("*, 1 as depth "))
->withRecursiveExpression('search_graph', $subquery)
->get()->map([Edge::class, 'fromDB']));
->get());
}
/**
* @param array<string> $ids
* @return Collection<Edge>
* @return Collection<stdClass>
*/
public function getParents(array $ids, QueryOptions $options): Collection
public function getParents(array $ids, Builder $options): Collection
{
$subquery = DB::table('edges AS g')
->select(DB::raw('g.source,g.target,g.rank,"g"."sourceSchema","g"."targetSchema",g.field, 1 as depth '))
->select(DB::raw('g.source,g.target,g.rank,"g"."sourceSchema","g"."targetSchema",g.field,g.data, 1 as depth '))
->whereIn('g.target', $ids);
$subquery = $options->resolveEdgeFilters($subquery);
if (!empty($options->parentFields)) {
$subquery->whereIn('field', $options->parentFields);
}
@@ -57,7 +60,7 @@ class PgsqlDatabaseGraph implements DatabaseGraph
->limit($options->parentsLimit)
->union(
DB::table(DB::raw("edges AS g, search_graph AS sg "))
->selectRaw('g.source,g.target,g.rank,"g"."sourceSchema","g"."targetSchema",g.field,sg.depth + 1 as depth')
->selectRaw('g.source,g.target,g.rank,"g"."sourceSchema","g"."targetSchema",g.field,g.data,sg.depth + 1 as depth')
->whereRaw("g.target = sg.source")
->where("depth", "<", $options->parentsDepth)
->orderBy("rank")
@@ -66,6 +69,6 @@ class PgsqlDatabaseGraph implements DatabaseGraph
return new Collection(DB::table('search_graph')
// ->select(DB::raw('sg.source,sg.target,sg.rank,sg."sourceSchema",sg."targetSchema",sg.field,sg.depth'))
->withRecursiveExpression('search_graph', $subquery)
->get()->map([Edge::class, 'fromDB']));
->get());
}
}
+17 -12
View File
@@ -3,22 +3,26 @@
namespace Lucent\Query\DatabaseGraph;
use Illuminate\Support\Facades\DB;
use Lucent\Edge\Edge;
use Lucent\Query\QueryOptions;
use Lucent\Query\Builder;
use Lucent\Support\Collection;
use stdClass;
class SqliteDatabaseGraph implements DatabaseGraph
{
/**
* @param array<string> $ids
* @return Collection<Edge>
* @return Collection<stdClass>
*/
public function getChildren(array $ids, QueryOptions $options): Collection
public function getChildren(array $ids, Builder $options):Collection
{
$subquery = DB::table('edges AS g')
->select(DB::raw('g.source,g.target,g.rank,g.sourceSchema,g.targetSchema,g.field, 1 as depth '))
->select(DB::raw('g.source,g.target,g.rank,g.sourceSchema,g.targetSchema,g.field,g.data, 1 as depth '))
->whereIn('source', $ids);
$subquery = $options->resolveEdgeFilters($subquery);
if (!empty($options->childrenFields)) {
$subquery->whereIn('field', $options->childrenFields);
}
@@ -26,7 +30,7 @@ class SqliteDatabaseGraph implements DatabaseGraph
$subquery->limit($options->childrenLimit)
->union(
DB::table(DB::raw("edges AS g, search_graph AS sg "))
->selectRaw('g.source,g.target,g.rank,"g"."sourceSchema","g"."targetSchema",g.field,sg.depth + 1 as depth')
->selectRaw('g.source,g.target,g.rank,"g"."sourceSchema","g"."targetSchema",g.field,g.data,sg.depth + 1 as depth')
->whereRaw("g.source = sg.target")
->where("depth", "<", $options->childrenDepth)
->orderBy("rank")
@@ -35,19 +39,20 @@ class SqliteDatabaseGraph implements DatabaseGraph
return new Collection(DB::table('search_graph')
// ->select(DB::raw("*, 1 as depth "))
->withRecursiveExpression('search_graph', $subquery)
->get()->map([Edge::class, 'fromDB']));
->get());
}
/**
* @param array<string> $ids
* @return Collection<Edge>
* @return Collection<stdClass>
*/
public function getParents(array $ids, QueryOptions $options): Collection
public function getParents(array $ids, Builder $options):Collection
{
$subquery = DB::table('edges AS g')
->select(DB::raw('g.source,g.target,g.rank,"g"."sourceSchema","g"."targetSchema",g.field, 1 as depth '))
->select(DB::raw('g.source,g.target,g.rank,"g"."sourceSchema","g"."targetSchema",g.field,g.data, 1 as depth '))
->whereIn('g.target', $ids);
$subquery = $options->resolveEdgeFilters($subquery);
if (!empty($options->parentFields)) {
$subquery->whereIn('field', $options->parentFields);
}
@@ -56,7 +61,7 @@ class SqliteDatabaseGraph implements DatabaseGraph
->limit($options->parentsLimit)
->union(
DB::table(DB::raw("edges AS g, search_graph AS sg "))
->selectRaw('g.source,g.target,g.rank,"g"."sourceSchema","g"."targetSchema",g.field,sg.depth + 1 as depth')
->selectRaw('g.source,g.target,g.rank,"g"."sourceSchema","g"."targetSchema",g.field,g.data,sg.depth + 1 as depth')
->whereRaw("g.target = sg.source")
->where("depth", "<", $options->parentsDepth)
->orderBy("rank")
@@ -65,6 +70,6 @@ class SqliteDatabaseGraph implements DatabaseGraph
return new Collection(DB::table('search_graph')
// ->select(DB::raw('sg.source,sg.target,sg.rank,sg."sourceSchema",sg."targetSchema",sg.field,sg.depth'))
->withRecursiveExpression('search_graph', $subquery)
->get()->map([Edge::class, 'fromDB']));
->get());
}
}
+51 -118
View File
@@ -2,29 +2,24 @@
namespace Lucent\Query;
use Illuminate\Contracts\Foundation\Application;
use Illuminate\Database\Query\Builder;
use Illuminate\Support\Facades\DB;
use Lucent\Query\Filter\AndFilter;
use Lucent\Query\Filter\Argument;
use Lucent\Query\Filter\Filter;
use Lucent\Query\Filter\OrFilter;
use Lucent\Query\Data\Argument;
final class FilterParser
{
public function __construct(public Application $app)
public function __construct()
{
}
/**
* @param array $arguments
* @return array<Argument>
*/
private function formatArguments(array $arguments): array
public function formatArguments(array $arguments): array
{
return collect($arguments)->reduce(function ($c, $v, $k) {
$c[] = $this->formatArgument($v, $k);
return $c;
}, []);
@@ -32,7 +27,6 @@ final class FilterParser
private function formatArgument(mixed $value, string $filter): Argument
{
$operator = $this->detectOperator($filter);
$field = $this->detectField($filter, $operator);
@@ -92,21 +86,21 @@ final class FilterParser
private function formatListNum(mixed $value): array
{
if (\is_string($value)) {
if (is_string($value)) {
$value = explode(",", $value);
}
return \array_map(fn($v) => $this->formatNumber($v), $value);
return array_map(fn($v) => $this->formatNumber($v), $value);
}
private function detectOperator(string $filter): string
{
$exploded = \explode("_", $filter);
$exploded = explode("_", $filter);
$candidate = end($exploded);
$operatorsListNames = collect(Operator::list())->map(fn($o) => $o->name)->toArray();
if (\in_array($candidate, $operatorsListNames)) {
if (in_array($candidate, $operatorsListNames)) {
return $candidate;
}
return 'eq';
@@ -123,29 +117,22 @@ final class FilterParser
return $filter;
}
private function formatReferences(array $referenceArguments): Argument
public function formatEdgesArguments(array $edgesArguments): Argument
{
$subqueries = collect($referenceArguments)->reduce(function ($c, $v, $k) {
$keyWithoutRef = str_replace("children.", "", $k);
$subqueries = collect($edgesArguments)->reduce(function ($c, $v, $k) {
$keyWithoutRef = str_replace("edges.", "", $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 = DB::table("edges")->whereIn("target", $targetIds)->where("field", $k)->get()->pluck("source");
$laravelBuilder = DB::table("edges");
$queryBuilder = new Builder();
$queryBuilder->filter($subquery);
$laravelBuilder = $queryBuilder->resolveRecordFilters($laravelBuilder);
$sourceIds = $laravelBuilder->where("field", $k)->get()->pluck("source");
return array_merge($c, $sourceIds->toArray());
}, []);
@@ -157,96 +144,42 @@ final class FilterParser
}
private function separateMainFromReferenceArguments(Filter $arguments): array
public function formatReferencesArguments(array $referenceArguments): Argument
{
return collect($arguments->toArray())->partition(function ($v, $k) {
if (!str_starts_with($k, "children.")) {
return true;
$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) {
$laravelBuilder = DB::table("records");
$queryBuilder = new Builder();
$queryBuilder->filter($subquery);
$laravelBuilder = $queryBuilder->resolveRecordFilters($laravelBuilder);
$res = $laravelBuilder->get();
if ($res->isEmpty()) {
return $c;
}
return false;
})->toArray();
$targetIds = $res->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",
operator: "in",
value: $sourceIds
);
}
/**
* @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
{
foreach ($arguments as $argument) {
if ($argument->operator == "in") {
$builder->whereIn($argument->field, $argument->value);
} else if ($argument->operator == "nin") {
$builder->whereNotIn($argument->field, $argument->value);
} else if ($argument->operator == "exists") {
$builder->where($argument->field, "!=", "");
$builder->where($argument->field, "!=", null);
} elseif ($argument->operator == "filter") {
$builder->whereJsonContains($argument->field, [$argument->value]);
// target result
// filter[data.previousNames_object]=previousNames&filter[previousNames.name_eq]=alpha&filter[previousNames.id_eqnum]=24
// $query->whereJsonContains("data->previousNames", [["name" => "alpha", "id" => 24]]);
// $query->whereJsonContains($filter["field"], [$objectFilters]);
} else {
$builder->where($argument->field, $argument->operator, $argument->value);
}
}
return $builder;
}
/**
* @param array<Argument> $arguments
*/
private function parseOr(Builder $builder, array $arguments): Builder
{
$builder->where(function (Builder $orBuilder) use ($arguments) {
foreach ($arguments as $argument) {
if ($argument->operator == "in") {
$orBuilder->orWhereIn($argument->field, $argument->value);
} else if ($argument->operator == "nin") {
$orBuilder->orWhereNotIn($argument->field, $argument->value);
} else if ($argument->operator == "exists") {
$orBuilder->where($argument->field, "!=", "");
$orBuilder->where($argument->field, "!=", null);
} elseif ($argument->operator == "filter") {
$orBuilder->whereJsonContains($argument->field, [$argument->value]);
// target result
// filter[data.previousNames_object]=previousNames&filter[previousNames.name_eq]=alpha&filter[previousNames.id_eqnum]=24
// $query->whereJsonContains("data->previousNames", [["name" => "alpha", "id" => 24]]);
// $query->whereJsonContains($filter["field"], [$objectFilters]);
} else {
$orBuilder->orWhere($argument->field, $argument->operator, $argument->value);
}
}
});
return $builder;
}
}
+9 -9
View File
@@ -2,9 +2,9 @@
namespace Lucent\Query;
use Lucent\Edge\Edge;
use Lucent\Record\QueryRecord;
use Lucent\Record\Record;
use Lucent\Graph\Edge\Edge;
use Lucent\Graph\Record\QueryRecord;
use Lucent\Graph\Record\Record;
use Lucent\Support\Collection;
use Lucent\Support\Option\Option;
@@ -21,7 +21,7 @@ final class Graph
public Collection $records,
public Collection $edges,
public Collection $parentEdges,
public QueryOptions $queryOptions,
public Builder $queryBuilder,
public ?int $total = null,
)
{
@@ -40,7 +40,7 @@ final class Graph
public function hasResults(): bool
{
return !empty($this->records);
return $this->rootRecords->isNotEmpty();
}
public function tree(): Collection
@@ -54,16 +54,16 @@ final class Graph
public function findChildren(QueryRecord $record, int $depth = 1): QueryRecord
{
if ($this->queryOptions->childrenDepth < $depth) {
if ($this->queryBuilder->childrenDepth < $depth) {
return $record;
}
$record->_children = $this->edges
->filter(fn(Edge $ed) => $ed->source === $record->record->id)
->unique(fn(Edge $ed) => $ed->targetSchema . $ed->field . $ed->target . $ed->source)
->unique(fn(Edge $ed) => $ed->field . $ed->target . $ed->source)
->sort(fn($a, $b) => $a->rank <=> $b->rank)
->values()
->map(function (Edge $edge): Option {
$records = $this->records->filter(fn(Record $rec) => $rec->id == $edge->target)->values();
$records = $this->records->filter(fn(Record $rec) => $rec->id === $edge->target)->values();
if ($records->isEmpty()) {
return none();
}
@@ -82,7 +82,7 @@ final class Graph
public function findParents(QueryRecord $record, int $depth = 1): QueryRecord
{
if ($this->queryOptions->parentsDepth < $depth) {
if ($this->queryBuilder->parentsDepth < $depth) {
return $record;
}
$record->_parents = $this->parentEdges
+1 -1
View File
@@ -6,7 +6,7 @@ namespace Lucent\Query;
final class Operator
{
/**
* @psalm-param string[] $uis
* @param string[] $uis
*/
public function __construct(
public string $name,
+39 -145
View File
@@ -2,63 +2,41 @@
namespace Lucent\Query;
use Illuminate\Database\Query\Builder;
use Illuminate\Database\Query\Builder as LaravelBuilder;
use Illuminate\Support\Facades\DB;
use Lucent\Graph\Record\Mapper;
use Lucent\Graph\Edge\Mapper as EdgeMapper;
use Lucent\Query\DatabaseGraph\DatabaseGraph;
use Lucent\Query\Filter\AndFilter;
use Lucent\Query\Filter\OrFilter;
use Lucent\Record\InputFormatter;
use Lucent\Record\Mapper;
use Lucent\Record\Record;
use Lucent\Support\Collection;
final class Query
final class Query
{
/**
* @var array<AndFilter> $filters
*/
public array $filters;
public QueryOptions $options;
public function __construct(
public readonly FilterParser $filterParser,
public readonly InputFormatter $inputFormatter,
public readonly DatabaseGraph $databaseGraph,
public readonly Mapper $recordMapper,
public readonly FilterParser $filterParser,
public readonly DatabaseGraph $databaseGraph,
public readonly Mapper $recordMapper,
public readonly EdgeMapper $edgeMapper,
)
{
$this->options = new QueryOptions();
}
public function filter(array $filterArguments): Query
public function run(callable $builderFunc): Graph
{
$this->filters[] = new AndFilter($filterArguments);
return $this;
}
public function orFilter(array $filterArguments): Query
{
$this->filters[] = new OrFilter($filterArguments);
return $this;
}
public function run(): Graph
{
$rootRecords = $this->mainQuery();
$builder = $builderFunc(new Builder());
[$rootRecords, $total] = $this->mainQuery($builder);
$ids = $rootRecords->pluck("id");
$resultChildrenEdgesTargetIds = [];
$resultChildrenEdges = new Collection();
if ($this->options->childrenDepth > 0 && $ids->isNotEmpty()) {
$resultChildrenEdges = $this->databaseGraph->getChildren($ids->toArray(), $this->options);
if ($builder->childrenDepth > 0 && $ids->isNotEmpty()) {
$resultChildrenEdges = $this->databaseGraph->getChildren($ids->toArray(), $builder)->map([$this->edgeMapper, 'fromDB']);
$resultChildrenEdgesTargetIds = $resultChildrenEdges->pluck("target");
}
$resultParentSourceTargetIds = [];
$resultParentEdges = new Collection();
if ($this->options->parentsDepth > 0 && $ids->isNotEmpty()) {
$resultParentEdges = $this->databaseGraph->getParents($ids->toArray(), $this->options);
if ($builder->parentsDepth > 0 && $ids->isNotEmpty()) {
$resultParentEdges = $this->databaseGraph->getParents($ids->toArray(), $builder)->map([$this->edgeMapper, 'fromDB']);
$resultParentSourceTargetIds = $resultParentEdges->pluck("source");
}
@@ -67,7 +45,7 @@ final class Query
if (!empty($edgesIds)) {
$edgeRecords = new Collection(DB::table('records')
->whereIn("id", $edgesIds)
->whereIn("status", $this->options->status)
->whereIn("status", $builder->status)
->get()->map([$this->recordMapper, 'fromDB']));
}
@@ -76,127 +54,43 @@ final class Query
$edgeRecords,
$resultChildrenEdges,
$resultParentEdges,
$this->options
$builder,
$total
);
$this->reset();
return $graph;
}
private function reset(): void
{
$this->options = new QueryOptions();
$this->filters = [];
}
public function tree(): Collection
{
return $this->run()->tree();
}
private function parseFilters(Builder $query): Builder
{
foreach ($this->filters as $filter) {
$query = $this->filterParser->parse($query, $filter);
}
$query->whereIn("status", $this->options->status);
return $query;
}
/**
* @return Collection<Record>
* @return array[Collection<Record>,?int]
*/
private function mainQuery(): Collection
private function mainQuery(Builder $builder): array
{
$query = DB::table("records");
$query = $this->parseFilters($query);
if ($this->options->limit > 0) {
$query->limit($this->options->limit);
$query->offset($this->options->skip);
$query = $builder->resolveRecordFilters($query);
$query = $builder->resolveReferenceFilters($query);
$query = $builder->resolveEdgeFilters($query);
$query->whereIn("status", $builder->status);
$total = null;
if ($builder->withCount) {
$total = $query->count();
}
$query = $this->orderByQuery($query);
return new Collection($query->get()->map([$this->recordMapper, 'fromDB']));
if ($builder->limit > 0) {
$query->limit($builder->limit);
$query->offset($builder->skip);
}
$query = $this->orderByQuery($query, $builder);
return [new Collection($query->get()->map([$this->recordMapper, 'fromDB'])), $total];
}
public
function runWithCount(): Graph
public function orderByQuery(LaravelBuilder $query, Builder $builder): LaravelBuilder
{
$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;
}
public function childrenFields(array $fields): Query
{
$this->options->childrenFields = $fields;
return $this;
}
public function parentFields(array $fields): Query
{
$this->options->parentFields = $fields;
return $this;
}
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) {
foreach ($builder->sort as $item) {
$field = str_replace(".", "->", ltrim($item, '-'));
$dir = str_starts_with($item, '-') ? "desc" : "asc";
if ($field) {
-25
View File
@@ -1,25 +0,0 @@
<?php
namespace Lucent\Query;
final class QueryOptions
{
public function __construct(
public int $limit = 20,
public int $skip = 0,
public int $childrenDepth = -1,
public int $parentsDepth = -1,
public int $childrenLimit = -1,
public int $parentsLimit = -1,
public array $childrenFields = [],
public array $parentFields = [],
public array $sort = [],
public array $status = ["published", "draft"]
)
{
}
}
+1 -1
View File
@@ -14,7 +14,7 @@ use Throwable;
function result(Result $result, ?int $successCode = null, ?int $errorCode = null): Response
{
return match (get_class($result)) {
Success::class => response($result->success()->get(), $successCode ?? 200),
Success::class => response(toArray($result->success()->get()), $successCode ?? 200),
Error::class => response([
"error" => $result->error()->get()
], $errorCode ?? 400)
+10 -18
View File
@@ -3,12 +3,10 @@
namespace Lucent\Revision;
use Illuminate\Support\Str;
use Lucent\Edge\EdgeCollection;
use Lucent\Record\FileInfo;
use Lucent\Record\Record;
use Lucent\Record\RecordData;
use Lucent\Record\System;
use PhpOption\Option;
use Lucent\Graph\Data\FieldData;
use Lucent\Graph\Edge\EdgeCollection;
use Lucent\Graph\Record\Record;
use Lucent\Graph\Record\System;
readonly class Revision
{
@@ -16,20 +14,16 @@ readonly class Revision
/**
* @param string $id
* @param string $recordId
* @param string $schema
* @param System $_sys
* @param RecordData $data
* @param FieldData $data
* @param EdgeCollection $_edges
* @param Option<FileInfo> $_file
*/
function __construct(
public string $id,
public string $recordId,
public string $schema,
public System $_sys,
public RecordData $data,
public EdgeCollection $_edges,
public Option $_file,
public string $id,
public string $recordId,
public System $_sys,
public FieldData $data,
public EdgeCollection $_edges,
)
{
@@ -45,11 +39,9 @@ readonly class Revision
return new Revision(
id: (string)Str::uuid(),
recordId: $record->id,
schema: $record->schema,
_sys: $record->_sys,
data: $record->data,
_edges: $edges,
_file: empty($record->_file) ? none() : some($record->_file)
);
}
+5 -11
View File
@@ -3,10 +3,8 @@
namespace Lucent\Revision;
use Illuminate\Support\Facades\DB;
use Lucent\Edge\EdgeCollection;
use Lucent\Record\FileInfo;
use Lucent\Record\RecordData;
use Lucent\Record\System;
use Lucent\Graph\Data\FieldData;
use Lucent\Graph\Record\System;
use Lucent\Support\Collection;
use PhpOption\Option;
use stdClass;
@@ -77,11 +75,9 @@ class RevisionRepo
return [
"id" => $revision->id,
"recordId" => $revision->recordId,
"schema" => $revision->schema,
"_sys" => json_encode($revision->_sys),
"_file" => $revision->_file->map(fn($v) => json_encode($v))->getOrElse(null),
"data" => json_encode($revision->data),
"_edges" => $revision->_edges->getOrElse(null)->toJson(),
"_edges" => $revision->_edges->toJson(),
];
}
@@ -92,11 +88,9 @@ class RevisionRepo
return new Revision(
id: $data->id,
recordId: $data->recordId,
schema: $data->schema,
_sys: System::fromArray(json_decode($data->_sys, true)),
data: new RecordData(json_decode($data->data, true)),
_edges: Option::fromValue($data->_edges)->map([EdgeCollection::class,'fromJson']),
_file: Option::fromValue($data->_file)->map([FileInfo::class,'fromJSON'])
data: new FieldData(json_decode($data->data, true)),
_edges: $data->_edges->fromJson($data->_edges),
);
}
}
+6 -6
View File
@@ -3,8 +3,8 @@
namespace Lucent\Revision;
use Lucent\Channel\ChannelService;
use Lucent\Edge\EdgeCollection;
use Lucent\Record\Record;
use Lucent\Graph\Edge\EdgeCollection;
use Lucent\Graph\Record\Record;
use Lucent\Support\Collection;
use PhpOption\Option;
@@ -13,7 +13,7 @@ readonly class RevisionService
public function __construct(
private ChannelService $channelService,
private RevisionRepo $revisionRepo,
private RevisionRepo $revisionRepo,
)
{
}
@@ -42,15 +42,15 @@ readonly class RevisionService
* @param EdgeCollection $edges
* @return void
*/
public function create(Record $record,EdgeCollection $edges): void
public function create(Record $record, EdgeCollection $edges): void
{
$schema = $this->channelService->getSchema($record->schema)->get();
if($schema->revisions <= 0){
if ($schema->revisions <= 0) {
return;
}
$revision = Revision::fromRecord($record, $edges);
$this->revisionRepo->create($revision);
$this->revisionRepo->cleanupRecord($record->id,$schema->revisions);
$this->revisionRepo->cleanupRecord($record->id, $schema->revisions);
}
+2 -2
View File
@@ -2,7 +2,7 @@
namespace Lucent\Schema\BlockUi;
use Lucent\Record\RecordData;
use Lucent\Graph\Data\FieldData;
use Lucent\Schema\Field\FieldDataInterface;
use Lucent\Schema\Field\FieldInfo;
use Lucent\Schema\Field\FieldInterface;
@@ -23,7 +23,7 @@ class Heading implements FieldInterface,FieldDataInterface
$this->info = new FieldInfo("heading", "Heading", FieldType::STRING);
}
public function format(RecordData $input, RecordData $output): RecordData
public function format(FieldData $input, FieldData $output): FieldData
{
$value = $input->get($this->name);
$output->set($this->name,$value);
+2 -2
View File
@@ -2,7 +2,7 @@
namespace Lucent\Schema\BlockUi;
use Lucent\Record\RecordData;
use Lucent\Graph\Data\FieldData;
use Lucent\Schema\Field\FieldDataInterface;
use Lucent\Schema\Field\FieldInfo;
use Lucent\Schema\Field\FieldInterface;
@@ -23,7 +23,7 @@ class Markdown implements FieldInterface,FieldDataInterface
$this->info = new FieldInfo("markdown", "Markdown Editor", FieldType::STRING);
}
public function format(RecordData $input, RecordData $output): RecordData
public function format(FieldData $input, FieldData $output): FieldData
{
$value = $input->get($this->name);
$output->set($this->name,$value);
+2 -2
View File
@@ -2,7 +2,7 @@
namespace Lucent\Schema\BlockUi;
use Lucent\Record\RecordData;
use Lucent\Graph\Data\FieldData;
use Lucent\Schema\Field\FieldDataInterface;
use Lucent\Schema\Field\FieldInfo;
use Lucent\Schema\Field\FieldInterface;
@@ -23,7 +23,7 @@ class Rich implements FieldInterface,FieldDataInterface
$this->info = new FieldInfo("rich", "Rich Editor", FieldType::STRING);
}
public function format(RecordData $input, RecordData $output): RecordData
public function format(FieldData $input, FieldData $output): FieldData
{
$value = $input->get($this->name);
$output->set($this->name,$value);
+2 -2
View File
@@ -2,7 +2,7 @@
namespace Lucent\Schema\BlockUi;
use Lucent\Record\RecordData;
use Lucent\Graph\Data\FieldData;
use Lucent\Schema\Field\FieldDataInterface;
use Lucent\Schema\Field\FieldInfo;
use Lucent\Schema\Field\FieldInterface;
@@ -23,7 +23,7 @@ class Textarea implements FieldInterface,FieldDataInterface
$this->info = new FieldInfo("textarea", "Textarea", FieldType::STRING);
}
public function format(RecordData $input, RecordData $output): RecordData
public function format(FieldData $input, FieldData $output): FieldData
{
$value = $input->get($this->name);
$output->set($this->name,$value);
+2 -2
View File
@@ -3,11 +3,11 @@
namespace Lucent\Schema\Field;
use Lucent\Record\RecordData;
use Lucent\Graph\Data\FieldData;
interface FieldDataInterface
{
public function format(RecordData $input, RecordData $output): RecordData;
public function format(FieldData $input, FieldData $output): FieldData;
public function isRequired(): bool;
+2 -2
View File
@@ -2,10 +2,10 @@
namespace Lucent\Schema\Ui;
use Lucent\Graph\Data\FieldData;
use Lucent\JsonSchema\Property\Property;
use Lucent\JsonSchema\Property\PropertyType;
use Lucent\JsonSchema\Property\TypeProperty;
use Lucent\Record\RecordData;
use Lucent\Schema\Field\FieldDataInterface;
use Lucent\Schema\Field\FieldInfo;
use Lucent\Schema\Field\FieldInterface;
@@ -31,7 +31,7 @@ class Block implements FieldInterface, FieldDataInterface, RequiredInterface
$this->info = new FieldInfo("block", "Block editor", FieldType::JSON);
}
public function format(RecordData $input, RecordData $output): RecordData
public function format(FieldData $input, FieldData $output): FieldData
{
$value = $input->get($this->name);
+2 -2
View File
@@ -2,10 +2,10 @@
namespace Lucent\Schema\Ui;
use Lucent\Graph\Data\FieldData;
use Lucent\JsonSchema\Property\Property;
use Lucent\JsonSchema\Property\PropertyType;
use Lucent\JsonSchema\Property\TypeProperty;
use Lucent\Record\RecordData;
use Lucent\Schema\Field\FieldDataInterface;
use Lucent\Schema\Field\FieldInfo;
use Lucent\Schema\Field\FieldInterface;
@@ -33,7 +33,7 @@ class Checkbox implements FieldInterface,FieldDataInterface, RequiredInterface
$this->info = new FieldInfo("checkbox", "Block Checkbox", FieldType::BOOLEAN);
}
public function format(RecordData $input, RecordData $output): RecordData
public function format(FieldData $input, FieldData $output): FieldData
{
$value = $input->get($this->name);
+2 -2
View File
@@ -2,10 +2,10 @@
namespace Lucent\Schema\Ui;
use Lucent\Graph\Data\FieldData;
use Lucent\JsonSchema\Property\Property;
use Lucent\JsonSchema\Property\PropertyType;
use Lucent\JsonSchema\Property\TypeProperty;
use Lucent\Record\RecordData;
use Lucent\Schema\Field\FieldDataInterface;
use Lucent\Schema\Field\FieldInfo;
use Lucent\Schema\Field\FieldInterface;
@@ -34,7 +34,7 @@ class Color implements FieldInterface,FieldDataInterface, RequiredInterface
$this->info = new FieldInfo("color", "Color", FieldType::STRING);
}
public function format(RecordData $input, RecordData $output): RecordData
public function format(FieldData $input, FieldData $output): FieldData
{
$value = $input->get($this->name);
$output->set($this->name,$value);
+2 -2
View File
@@ -3,10 +3,10 @@
namespace Lucent\Schema\Ui;
use Carbon\Carbon;
use Lucent\Graph\Data\FieldData;
use Lucent\JsonSchema\Property\Property;
use Lucent\JsonSchema\Property\PropertyType;
use Lucent\JsonSchema\Property\TypeProperty;
use Lucent\Record\RecordData;
use Lucent\Schema\Field\FieldDataInterface;
use Lucent\Schema\Field\FieldInfo;
use Lucent\Schema\Field\FieldInterface;
@@ -37,7 +37,7 @@ class Date implements FieldInterface,FieldDataInterface, RequiredInterface, MinM
$this->info = new FieldInfo("date", "Date", FieldType::STRING);
}
public function format(RecordData $input, RecordData $output): RecordData
public function format(FieldData $input, FieldData $output): FieldData
{
$value = $input->get($this->name);
if (empty($value)) {
+2 -2
View File
@@ -3,10 +3,10 @@
namespace Lucent\Schema\Ui;
use Carbon\Carbon;
use Lucent\Graph\Data\FieldData;
use Lucent\JsonSchema\Property\Property;
use Lucent\JsonSchema\Property\PropertyType;
use Lucent\JsonSchema\Property\TypeProperty;
use Lucent\Record\RecordData;
use Lucent\Schema\Field\FieldDataInterface;
use Lucent\Schema\Field\FieldInfo;
use Lucent\Schema\Field\FieldInterface;
@@ -37,7 +37,7 @@ class Datetime implements FieldInterface,FieldDataInterface, RequiredInterface,
$this->info = new FieldInfo("datetime", "Datetime", FieldType::STRING);
}
public function format(RecordData $input, RecordData $output): RecordData
public function format(FieldData $input, FieldData $output): FieldData
{
$value = $input->get($this->name);
if (empty($value)) {
+2 -2
View File
@@ -2,10 +2,10 @@
namespace Lucent\Schema\Ui;
use Lucent\Graph\Data\FieldData;
use Lucent\JsonSchema\Property\Property;
use Lucent\JsonSchema\Property\PropertyType;
use Lucent\JsonSchema\Property\TypeProperty;
use Lucent\Record\RecordData;
use Lucent\Schema\Field\FieldDataInterface;
use Lucent\Schema\Field\FieldInfo;
use Lucent\Schema\Field\FieldInterface;
@@ -32,7 +32,7 @@ class Json implements FieldInterface,FieldDataInterface, RequiredInterface
$this->info = new FieldInfo("json", "JSON", FieldType::JSON);
}
public function format(RecordData $input, RecordData $output): RecordData
public function format(FieldData $input, FieldData $output): FieldData
{
$value = $input->get($this->name);
+2 -2
View File
@@ -2,10 +2,10 @@
namespace Lucent\Schema\Ui;
use Lucent\Graph\Data\FieldData;
use Lucent\JsonSchema\Property\Property;
use Lucent\JsonSchema\Property\PropertyType;
use Lucent\JsonSchema\Property\TypeProperty;
use Lucent\Record\RecordData;
use Lucent\Schema\Field\FieldDataInterface;
use Lucent\Schema\Field\FieldInfo;
use Lucent\Schema\Field\FieldInterface;
@@ -32,7 +32,7 @@ class Markdown implements FieldInterface,FieldDataInterface, RequiredInterface
$this->info = new FieldInfo("markdown", "Markdown editor", FieldType::STRING);
}
public function format(RecordData $input, RecordData $output): RecordData
public function format(FieldData $input, FieldData $output): FieldData
{
$value = $input->get($this->name);
$output->set($this->name,$value);
+2 -2
View File
@@ -2,10 +2,10 @@
namespace Lucent\Schema\Ui;
use Lucent\Graph\Data\FieldData;
use Lucent\JsonSchema\Property\Property;
use Lucent\JsonSchema\Property\PropertyType;
use Lucent\JsonSchema\Property\TypeProperty;
use Lucent\Record\RecordData;
use Lucent\Schema\Field\FieldDataInterface;
use Lucent\Schema\Field\FieldInfo;
use Lucent\Schema\Field\FieldInterface;
@@ -37,7 +37,7 @@ class Number implements FieldInterface, RequiredInterface,FieldDataInterface, Mi
$this->info = new FieldInfo("number", "Number", FieldType::NUMBER);
}
public function format(RecordData $input, RecordData $output): RecordData
public function format(FieldData $input, FieldData $output): FieldData
{
$value = $input->get($this->name);
if (!is_numeric($value)) {
+2 -2
View File
@@ -2,10 +2,10 @@
namespace Lucent\Schema\Ui;
use Lucent\Graph\Data\FieldData;
use Lucent\JsonSchema\Property\Property;
use Lucent\JsonSchema\Property\PropertyType;
use Lucent\JsonSchema\Property\TypeProperty;
use Lucent\Record\RecordData;
use Lucent\Schema\Field\FieldDataInterface;
use Lucent\Schema\Field\FieldInfo;
use Lucent\Schema\Field\FieldInterface;
@@ -32,7 +32,7 @@ class Rich implements FieldInterface,FieldDataInterface, RequiredInterface
$this->info = new FieldInfo("rich", "Rich editor", FieldType::STRING);
}
public function format(RecordData $input, RecordData $output): RecordData
public function format(FieldData $input, FieldData $output): FieldData
{
$value = $input->get($this->name);
$output->set($this->name,$value);
+2 -2
View File
@@ -3,10 +3,10 @@
namespace Lucent\Schema\Ui;
use Illuminate\Support\Str;
use Lucent\Graph\Data\FieldData;
use Lucent\JsonSchema\Property\Property;
use Lucent\JsonSchema\Property\PropertyType;
use Lucent\JsonSchema\Property\TypeProperty;
use Lucent\Record\RecordData;
use Lucent\Schema\Field\FieldDataInterface;
use Lucent\Schema\Field\FieldInfo;
use Lucent\Schema\Field\FieldInterface;
@@ -34,7 +34,7 @@ class Slug implements FieldInterface,FieldDataInterface, RequiredInterface
$this->info = new FieldInfo("slug", "Slug", FieldType::STRING);
}
public function format(RecordData $input, RecordData $output): RecordData
public function format(FieldData $input, FieldData $output): FieldData
{
$value = $input->get($this->name);
if (empty($value)) {
+2 -2
View File
@@ -2,10 +2,10 @@
namespace Lucent\Schema\Ui;
use Lucent\Graph\Data\FieldData;
use Lucent\JsonSchema\Property\Property;
use Lucent\JsonSchema\Property\PropertyType;
use Lucent\JsonSchema\Property\TypeProperty;
use Lucent\Record\RecordData;
use Lucent\Schema\Field\FieldDataInterface;
use Lucent\Schema\Field\FieldInfo;
use Lucent\Schema\Field\FieldInterface;
@@ -36,7 +36,7 @@ class Text implements FieldInterface,FieldDataInterface, RequiredInterface
$this->info = new FieldInfo("text", "Text", FieldType::STRING);
}
public function format(RecordData $input, RecordData $output): RecordData
public function format(FieldData $input, FieldData $output): FieldData
{
$value = $input->get($this->name);
$output->set($this->name,$value);
+2 -2
View File
@@ -2,10 +2,10 @@
namespace Lucent\Schema\Ui;
use Lucent\Graph\Data\FieldData;
use Lucent\JsonSchema\Property\Property;
use Lucent\JsonSchema\Property\PropertyType;
use Lucent\JsonSchema\Property\TypeProperty;
use Lucent\Record\RecordData;
use Lucent\Schema\Field\FieldDataInterface;
use Lucent\Schema\Field\FieldInfo;
use Lucent\Schema\Field\FieldInterface;
@@ -33,7 +33,7 @@ class Textarea implements FieldInterface,FieldDataInterface, RequiredInterface
$this->info = new FieldInfo("textarea", "Textarea", FieldType::STRING);
}
public function format(RecordData $input, RecordData $output): RecordData
public function format(FieldData $input, FieldData $output): FieldData
{
$value = $input->get($this->name);
$output->set($this->name,$value);
+2 -2
View File
@@ -2,10 +2,10 @@
namespace Lucent\Schema\Ui;
use Lucent\Graph\Data\FieldData;
use Lucent\JsonSchema\Property\Property;
use Lucent\JsonSchema\Property\PropertyType;
use Lucent\JsonSchema\Property\TypeProperty;
use Lucent\Record\RecordData;
use Lucent\Schema\Field\FieldDataInterface;
use Lucent\Schema\Field\FieldInfo;
use Lucent\Schema\Field\FieldInterface;
@@ -30,7 +30,7 @@ class Uuid implements FieldInterface,FieldDataInterface, RequiredInterface
$this->info = new FieldInfo("uuid", "FieldType", FieldType::STRING);
}
public function format(RecordData $input, RecordData $output): RecordData
public function format(FieldData $input, FieldData $output): FieldData
{
$value = $input->get($this->name);
$output->set($this->name,$value);
+5 -5
View File
@@ -3,7 +3,7 @@
namespace Lucent\Schema\Validator;
use Lucent\Channel\ChannelService;
use Lucent\Record\RecordData;
use Lucent\Graph\Data\FieldData;
use Lucent\Schema\Field\FieldDataInterface;
use Lucent\Support\Collection;
use Lucent\Support\Option\Option;
@@ -23,8 +23,8 @@ class Validator
* @return Collection<ValidatorError>
*/
public function check(
string $schemaName,
RecordData $data,
string $schemaName,
FieldData $data,
): Collection
{
@@ -39,10 +39,10 @@ class Validator
/**
* @param FieldDataInterface $field
* @param RecordData $recordData
* @param FieldData $recordData
* @return Option<ValidatorError>
*/
public function validate(FieldDataInterface $field, RecordData $recordData): Option
public function validate(FieldDataInterface $field, FieldData $recordData): Option
{
$value = $recordData->get($field->name);