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