edges wip

This commit is contained in:
2026-01-10 13:40:33 +02:00
parent ced6146266
commit b0485e77fc
14 changed files with 321 additions and 45 deletions
@@ -42,6 +42,7 @@
<RecordForm
{channel}
fields={data.fields}
edgeRecordPreviews={data.edgeRecordPreviewsDraft}
{record}
{selectedLocales}
validationErrors={data.validationErrors}
@@ -53,6 +54,7 @@
<RecordForm
{channel}
fields={data.fields}
edgeRecordPreviews={data.edgeRecordPreviewsLive}
{record}
{selectedLocales}
validationErrors={data.validationErrors}
@@ -7,9 +7,9 @@
channel,
validationErrors,
fieldData,
edgeRecordPreviews,
selectedLocales,
} = $props();
const findFieldValidationError = (field, locale) => {
return validationErrors.find(
(f) => f.fieldId === field.id && f.locale === locale,
@@ -20,6 +20,12 @@
(f) => f.fieldId === field.id && f.locale === locale,
);
};
$inspect(edgeRecordPreviews);
const findFieldEdges = (field, locale) => {
return edgeRecordPreviews.filter(
(e) => e.edge.fieldId === field.id && e.edge.locale === locale,
);
};
</script>
{#each fields as field}
@@ -62,5 +68,6 @@
schemaField={field}
{locale}
dataField={findDataField(field, locale)}
edgeRecordPreviews={findFieldEdges(field, locale)}
></RelationField>
{/snippet}
@@ -2,8 +2,15 @@
import { get, post } from "../../../modules/remote";
import { getApp } from "../../../app";
import { getLocaleName } from "../locale.svelte.js";
let { channel, record, schemaField, dataField, locale, validationError } =
$props();
let {
channel,
record,
schemaField,
dataField,
locale,
validationError,
edgeRecordPreviews,
} = $props();
let originalValue = dataField?.value ?? schemaField.props.default;
let newValue = $state(originalValue);
let valuesChanged = $derived(newValue !== originalValue);
@@ -14,9 +21,12 @@
let suggestionsLoaded = $state(false);
let suggestions = $state([]);
let selectedRecordIds = $state([]);
let dialog = $state();
function handleAddRecord(e) {
function handleModalOpen(e) {
// Add logic to handle adding a record
dialog.showModal();
if (suggestionsLoaded) {
return;
}
@@ -30,26 +40,24 @@
);
}
function save() {
if (!valuesChanged) {
return;
}
function handleModalClose(e) {
dialog.close();
}
function handleInsertSelected() {
suggestionsLoaded = false;
post(
app.url("records/fields"),
app.url("edges/many"),
{
recordId: record.id,
id: schemaField.id,
toIds: selectedRecordIds,
from: record.id,
fieldId: schemaField.id,
locale: locale,
value: newValue,
},
(data, err) => {
if (err.isNotEmpty()) {
errorMessage = err.first();
} else {
validationError = data.validationError;
originalValue = newValue;
}
suggestionsLoaded = true;
dialog.close();
edgeRecordPreviews = [...edgeRecordPreviews, ...data];
},
);
}
@@ -61,26 +69,68 @@
{getLocaleName(channel, locale)} >
{/if}
{schemaField.name} <br />
<details class="dropdown">
<summary onclick={handleAddRecord}>Add Record</summary>
</label>
{#if hasError}
<small id={schemaField.id + "-help"}>{validationError.message}</small>
{:else if schemaField.help != ""}
<small id={schemaField.id + "-help"}>{schemaField.help}</small>
{/if}
<button onclick={handleModalOpen}>Choose record</button>
<dialog bind:this={dialog}>
<article>
<header>
<button onclick={handleModalClose} aria-label="Close" rel="prev"
></button>
<p>
<strong>Records</strong>
</p>
</header>
{#if suggestionsLoaded}
<ul>
{#each suggestions as suggestion}
<li><a href="#">{suggestion.title}</a></li>
{/each}
<li><a href="#">More</a></li>
</ul>
<form>
<button onclick={handleInsertSelected}>
Insert selected
</button>
<table>
<tbody>
{#each suggestions as suggestion}
<tr>
<td>
<input
type="checkbox"
value={suggestion.id}
bind:group={selectedRecordIds}
/>
</td>
<td>
<a href="#">{suggestion.title}</a>
</td>
<td>
<a href="#">
{suggestion.schemaName}
</a>
</td>
</tr>
{/each}
</tbody>
</table>
</form>
{:else}
<progress />
{/if}
</details>
{#if hasError}
<small id={schemaField.id + "-help"}
>{validationError.message}</small
>
{:else if schemaField.help != ""}
<small id={schemaField.id + "-help"}>{schemaField.help}</small>
</article>
</dialog>
<div>
{#if edgeRecordPreviews.length == 0}
No relations exist
{:else}
{#each edgeRecordPreviews as edgeRecordPreview}
<div>
<a href="#">{edgeRecordPreview.recordPreview.title}</a>
{edgeRecordPreview.recordPreview.schemaName}
</div>
{/each}
{/if}
</label>
</div>
</div>
+14
View File
@@ -0,0 +1,14 @@
<?php namespace Lucent\Core\Data;
use Lucent\Core\Data\RecordMode;
class Edge
{
public function __construct(
public string $id,
public string $locale,
public RecordMode $mode,
public string $from,
public string $to,
public string $fieldId,
public int $rank,
) {}
}
+9
View File
@@ -0,0 +1,9 @@
<?php namespace Lucent\Core\Data;
class EdgeRecordPreview
{
public function __construct(
public Edge $edge,
public RecordPreview $recordPreview,
) {}
}
+1
View File
@@ -5,6 +5,7 @@ class RecordPreview
public function __construct(
public string $id,
public string $schemaId,
public string $schemaName,
public string $title,
) {}
}
+35
View File
@@ -0,0 +1,35 @@
<?php namespace Lucent\Core\Edge;
use Carbon\Carbon;
use Lucent\Core\Data\Edge;
use Lucent\Core\Data\RecordMode;
use stdClass;
class EdgeModule
{
public static function toDb(Edge $edge): array
{
return [
"id" => $edge->id,
"locale" => $edge->locale,
"mode" => $edge->mode->value,
"from" => $edge->from,
"to" => $edge->to,
"field_id" => $edge->fieldId,
"rank" => $edge->rank,
];
}
public static function fromDb(stdClass $data): Edge
{
return new Edge(
id: data_get($data, "id"),
locale: data_get($data, "locale"),
mode: RecordMode::from(data_get($data, "mode")),
from: data_get($data, "from"),
to: data_get($data, "to"),
fieldId: data_get($data, "field_id"),
rank: data_get($data, "rank"),
);
}
}
+37 -2
View File
@@ -4,6 +4,8 @@ use Carbon\Carbon;
use Lucent\Core\Data\Record;
use Lucent\Core\Data\RecordPreview;
use Lucent\Core\Data\RecordStatus;
use Lucent\Core\Data\EdgeRecordPreview;
use Lucent\Core\Edge\EdgeModule;
use stdClass;
class RecordModule
@@ -68,12 +70,45 @@ class RecordModule
);
}
public static function recordPreviewFromDb(stdClass $data): RecordPreview
{
/**
* @param Schema[] $schemas
* @param stdClass $data
* @return RecordPreview
*/
public static function recordPreviewFromDb(
array $schemas,
stdClass $data,
): RecordPreview {
return new RecordPreview(
id: data_get($data, "id"),
schemaId: data_get($data, "schema_id"),
schemaName: collect($schemas)
->where("id", data_get($data, "schema_id"))
->first()->name,
title: data_get($data, "title"),
);
}
/**
* @param Schema[] $schemas
* @param stdClass $data
* @return EdgeRecordPreview
*/
public static function edgeRecordPreviewFromDb(
array $schemas,
stdClass $data,
): EdgeRecordPreview {
return new EdgeRecordPreview(
edge: EdgeModule::fromDb($data),
recordPreview: new RecordPreview(
id: data_get($data, "record_id"),
schemaId: data_get($data, "record_schema_id"),
schemaName: collect($schemas)
->where("id", data_get($data, "record_schema_id"))
->first()->name,
title: data_get($data, "record_title"),
),
);
}
}
+22
View File
@@ -0,0 +1,22 @@
<?php namespace Lucent\Core\Repository;
use Illuminate\Support\Facades\DB;
use Lucent\Core\Data\Edge;
use Lucent\Core\Edge\EdgeModule;
class EdgeRepo
{
const TABLE_NAME = "edges";
/**
* Insert multiple edges into the database.
*
* @param Edge[] $edges An array of Edge objects to be inserted.
*/
public static function insertMany(array $edges): void
{
DB::table(self::TABLE_NAME)->insert(
array_map(EdgeModule::toDb(...), $edges),
);
}
}
+43 -3
View File
@@ -3,6 +3,7 @@
use Illuminate\Support\Facades\DB;
use Lucent\Core\Data\Record;
use Lucent\Core\Data\RecordPreview;
use Lucent\Core\Data\EdgeRecordPreview;
use Lucent\Core\Record\RecordModule;
class RecordRepo
@@ -30,18 +31,20 @@ class RecordRepo
->first();
}
/*
/**
* @param Schema[] $schemas
* @return RecordPreview[]
*/
public static function findBySchemas(array $schemas): array
{
$schemaIds = array_map(fn($schema) => $schema->id, $schemas);
return DB::table(self::TABLE_NAME)
->select(
"records.id",
"records.schema_id",
"records_data.value as title",
)
->whereIn("schema_id", $schemas)
->whereIn("schema_id", $schemaIds)
->join("records_data", function ($join) {
$join
->on("records.id", "=", "records_data.record_id")
@@ -54,8 +57,45 @@ class RecordRepo
->orderBy("created_at", "desc")
->limit(5)
->get()
->map(fn($row) => RecordModule::recordPreviewFromDb($schemas, $row))
->toArray();
}
->map(RecordModule::recordPreviewFromDb(...))
/**
* @param Schema[] $schemas
* @return EdgeRecordPreview[]
*/
public static function findEdgeRecordPreviewsByRecordId(
array $schemas,
string $recordId,
): array {
return DB::table("edges")
->select(
"edges.*",
"records.id as record_id",
"records.schema_id as record_schema_id",
"records_data.value as record_title",
)
->where("edges.from", $recordId)
->join("records", "edges.from", "=", "records.id")
->join("records_data", function ($join) {
$join
->on("records.id", "=", "records_data.record_id")
->on(
"records.title_field_id",
"=",
"records_data.field_id",
);
})
->orderBy("edges.rank", "asc")
->get()
->map(
fn($row) => RecordModule::edgeRecordPreviewFromDb(
$schemas,
$row,
),
)
->toArray();
}
}
+13
View File
@@ -20,6 +20,19 @@ class SchemaRepo
->toArray();
}
/**
* @return Schema[]
*/
public static function findByIds(array $schemaIds): array
{
return DB::table(self::TABLE_NAME)
->whereIn("id", $schemaIds)
->orderBy("name")
->get()
->map(SchemaModule::fromDb(...))
->toArray();
}
public static function findOne(string $id): ?Schema
{
return DB::table(self::TABLE_NAME)
+29 -3
View File
@@ -4,10 +4,36 @@ namespace Lucent\Http\Controller;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use Lucent\Edge\EdgeService;
use Lucent\Query\Query;
use Lucent\Core\Repository\EdgeRepo;
use Lucent\Core\Data\Edge;
use Lucent\Core\Data\RecordMode;
use Lucent\Id\Id;
use function Lucent\Response\fail;
use function Lucent\Response\ok;
class EdgeController extends Controller
{
public function postCreate(Request $request) {}
public function postCreateMany(Request $request)
{
$from = $request->input("from");
$toIds = $request->input("toIds");
$fieldId = $request->input("fieldId");
$locale = $request->input("locale");
$edges = array_map(
fn($to) => new Edge(
id: Id::new(),
from: $from,
to: $to,
fieldId: $fieldId,
locale: $locale,
rank: 0,
mode: RecordMode::DRAFT,
),
$toIds,
);
EdgeRepo::insertMany($edges);
return ok($edges);
}
}
+18 -2
View File
@@ -183,6 +183,19 @@ class RecordController
);
$recordStatus = RecordModule::getStatus($record);
$edgeRecordPreviews = RecordRepo::findEdgeRecordPreviewsByRecordId(
$schemas,
$record->id,
);
$edgeRecordPreviewsDraft = collect($edgeRecordPreviews)
->where("edge.mode", RecordMode::DRAFT)
->values()
->toArray();
$edgeRecordPreviewsLive = collect($edgeRecordPreviews)
->where("edge.mode", RecordMode::LIVE)
->values()
->toArray();
return Svelte::view(
view: "recordEdit",
@@ -191,7 +204,8 @@ class RecordController
"schemas" => $schemas,
"schema" => $schema,
"fields" => $fields,
// "graph" => toArray($graph),
"edgeRecordPreviewsDraft" => toArray($edgeRecordPreviewsDraft),
"edgeRecordPreviewsLive" => toArray($edgeRecordPreviewsLive),
"record" => toArray($record),
"draftData" => $draftData,
"liveData" => $liveData,
@@ -203,7 +217,9 @@ class RecordController
public function suggest(Request $request)
{
$schemas = $request->input("schemas");
$schemaIds = $request->input("schemas");
$schemas = SchemaRepo::findByIds($schemaIds);
$records = RecordRepo::findBySchemas($schemas);
return ok(toArray($records));
}
+6
View File
@@ -98,6 +98,12 @@ Route::group(
RecordController::class,
"untrash",
]);
// EDGES
Route::post("edges/many", [
EdgeController::class,
"postCreateMany",
]);
});
//OLD