Files
lucent-laravel/src/Http/Controller/RecordController.php
T

489 lines
15 KiB
PHP
Raw Normal View History

2023-10-02 23:10:49 +03:00
<?php
namespace Lucent\Http\Controller;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use Lucent\Account\AccountService;
use Lucent\Channel\ChannelService;
use Lucent\LucentException;
2024-08-24 17:22:40 +03:00
use Lucent\Query\Operator\OperatorRegistry;
2023-10-02 23:10:49 +03:00
use Lucent\Query\Query;
2024-08-19 17:48:10 +03:00
use Lucent\Record\InputData\EdgeInputData;
use Lucent\Record\InputData\RecordInputData;
2023-10-02 23:10:49 +03:00
use Lucent\Record\Manager;
use Lucent\Record\QueryRecord;
use Lucent\Record\RecordService;
2024-08-19 17:48:10 +03:00
use Lucent\Record\Status;
2023-10-04 13:32:30 +03:00
use Lucent\Schema\System;
2024-12-14 18:56:04 +02:00
use Lucent\Schema\Ui\Reference;
2023-10-02 23:10:49 +03:00
use Lucent\Schema\Validator\ValidatorException;
use Lucent\Svelte\Svelte;
2024-12-18 13:02:09 +02:00
use Lucent\ViewModel\ViewModel;
2023-10-02 23:10:49 +03:00
use function Lucent\Response\fail;
use function Lucent\Response\ok;
2026-01-08 18:50:32 +02:00
class RecordController
2023-10-02 23:10:49 +03:00
{
public function __construct(
2026-01-08 18:50:32 +02:00
private readonly RecordService $recordService,
private readonly AccountService $accountService,
private readonly ChannelService $channelService,
private readonly Svelte $svelte,
private readonly Query $query,
private readonly Manager $recordManager,
2024-12-18 13:02:09 +02:00
private readonly OperatorRegistry $operatorRegistry,
private readonly ViewModel $viewModel,
2026-01-08 18:50:32 +02:00
) {}
2023-10-02 23:10:49 +03:00
public function index(Request $request)
{
$schemaName = $request->route("schemaName");
2023-10-17 22:57:25 +03:00
2026-01-08 18:50:32 +02:00
if (
!in_array(
$schemaName,
$this->accountService->currentReadableSchemas(),
)
) {
return Svelte::view("recordNotFound", "Schema Not Found", []);
2023-10-17 22:57:25 +03:00
}
2023-10-02 23:10:49 +03:00
$users = $this->accountService->all();
2023-10-04 13:32:30 +03:00
$schema = $this->channelService->getSchema($schemaName)->get();
2023-10-02 23:10:49 +03:00
$urlParams = $request->all();
2023-10-23 19:43:59 +03:00
$sort = data_get($urlParams, "sort") ?? $schema->sortBy;
2023-10-02 23:10:49 +03:00
$filter = data_get($urlParams, "filter") ?? [];
2023-10-17 22:57:25 +03:00
2026-01-08 18:50:32 +02:00
$arguments = array_merge(
[
"schema" => $schema->name,
"status_in" => "draft,published",
],
$filter,
);
2024-09-07 15:31:56 +03:00
2023-10-02 23:10:49 +03:00
$skip = data_get($urlParams, "skip") ?? 0;
2023-10-23 22:12:17 +03:00
$limit = 30;
2023-10-02 23:10:49 +03:00
$records = [];
$graphArray = null;
2024-09-07 15:31:56 +03:00
2023-10-04 13:32:30 +03:00
$graph = $this->query
->filter($arguments)
2024-08-16 17:38:26 +03:00
->notLinked($request->input("notlinked") ?? "")
2023-10-04 13:32:30 +03:00
->limit($limit)
->status(explode(",", $arguments["status_in"]))
->skip($skip)
->sort($sort)
2024-01-15 17:33:24 +02:00
->childrenFields($schema?->visible ?? [])
2023-10-04 13:32:30 +03:00
->childrenDepth(1)
->parentsDepth(0)
->runWithCount();
$records = $graph->getRootRecords()->toArray();
2023-10-02 23:10:49 +03:00
$data = [
"schemas" => $this->channelService->channel->schemas,
"schema" => $schema,
"users" => $users,
"records" => $records,
2023-10-04 13:32:30 +03:00
"graph" => toArray($graph),
2023-10-02 23:10:49 +03:00
"systemFields" => array_values(System::list()),
2024-08-24 17:22:40 +03:00
"operators" => $this->operatorRegistry->all(),
2023-10-23 19:43:59 +03:00
"sortParam" => $sort,
2026-01-08 18:50:32 +02:00
"sortField" => $schema->fields
->merge(array_values(System::list()))
->firstWhere(
fn($field) => $field->name === $sort ||
"-" . $field->name === $sort ||
"data." . $field->name === $sort ||
"-data." . $field->name === $sort,
),
2023-10-02 23:10:49 +03:00
"limit" => $limit,
"skip" => $skip,
2023-10-04 13:32:30 +03:00
"total" => $graph->total ?? 0,
2023-10-02 23:10:49 +03:00
"filter" => $request->input("filter") ?? [],
"inModal" => true,
2026-01-08 18:50:32 +02:00
"isWritable" => in_array(
$schemaName,
$this->accountService->currentWritableSchemas(),
),
2023-10-02 23:10:49 +03:00
];
if ($request->ajax()) {
$data["modalUrl"] = $request->fullUrl();
2023-10-16 16:12:21 +03:00
if (str_starts_with(config("lucent.url"), "https")) {
2026-01-08 18:50:32 +02:00
$data["modalUrl"] = str_replace(
"http://",
"https://",
$request->fullUrl(),
);
2023-10-16 16:12:21 +03:00
}
2023-10-02 23:10:49 +03:00
return $data;
}
$data["inModal"] = false;
2026-01-08 18:50:32 +02:00
return Svelte::view(
2023-10-02 23:10:49 +03:00
view: "contentIndex",
title: "Records",
2026-01-08 18:50:32 +02:00
data: $data,
2023-10-02 23:10:49 +03:00
);
}
public function exportCSV(Request $request)
{
$schemaName = $request->route("schemaName");
2026-01-08 18:50:32 +02:00
$schema = $this->channelService->channel->schemas
->where("name", $schemaName)
->first();
2023-10-02 23:10:49 +03:00
$urlParams = $request->all();
$sort = data_get($urlParams, "sort") ?? "-_sys.updatedAt";
$filter = data_get($urlParams, "filter") ?? [];
2026-01-08 18:50:32 +02:00
$arguments = array_merge(
[
"schema" => $schema->name,
"status_in" => "draft,published",
],
$filter,
);
2023-10-02 23:10:49 +03:00
2023-10-08 02:12:02 +03:00
$records = $this->query
->filter($arguments)
->limit(-1)
->status(explode(",", $arguments["status_in"]))
2024-12-14 18:56:04 +02:00
->childrenDepth(1)
2023-10-08 02:12:02 +03:00
// ->skip($skip)
->sort($sort)
->run()
2024-12-14 18:56:04 +02:00
->tree();
2023-10-02 23:10:49 +03:00
2026-01-08 18:50:32 +02:00
header("Content-Type: application/csv");
header(
'Content-Disposition: attachment; filename="' .
$schemaName .
'.csv";',
);
$handle = fopen("php://output", "w");
2024-12-14 18:56:04 +02:00
$relationColumns = $this->makeCsvRelationColumns($schema);
2026-01-08 18:50:32 +02:00
$csvRow = [
"id",
...array_keys($records[0]->data->toArray()),
...$relationColumns,
];
fputcsv($handle, $csvRow, ",");
2023-10-02 23:10:49 +03:00
foreach ($records as $record) {
$csvRow = [$record->id, ...$record->data->toArray()];
2026-01-08 18:50:32 +02:00
$csvRow = array_merge(
$csvRow,
$this->makeCsvRelationColumnValues($schema, $record->_children),
);
2023-10-02 23:10:49 +03:00
$csvRow = array_values($csvRow);
2026-01-08 18:50:32 +02:00
fputcsv($handle, $csvRow, ",");
2023-10-02 23:10:49 +03:00
}
fclose($handle);
echo $handle;
2026-01-08 18:50:32 +02:00
exit();
2023-10-02 23:10:49 +03:00
}
2026-01-08 18:50:32 +02:00
private function makeCsvRelationColumns($schema): array
{
return $schema->fields
->filter(fn($f) => get_class($f) === Reference::class)
->reduce(function ($c, $f) {
$c[] = $f->name . " id";
$c[] = $f->name . " name";
return $c;
}, []);
2024-12-14 18:56:04 +02:00
}
2026-01-08 18:50:32 +02:00
private function makeCsvRelationColumnValues($schema, $children): array
{
return $schema->fields
->filter(fn($f) => get_class($f) === Reference::class)
->reduce(function ($c, $f) use ($children) {
$fieldRecords = data_get($children, $f->name);
if (empty($fieldRecords)) {
$c[] = "";
$c[] = "";
} elseif (count($fieldRecords) === 1) {
$c[] = data_get($fieldRecords, "0.id");
$c[] = $this->viewModel->getRecordName($fieldRecords[0]);
} else {
$c[] = collect($fieldRecords)->pluck("id")->join("::");
$c[] = collect($fieldRecords)
->pluck("data.name")
->join("::");
}
return $c;
}, []);
2024-12-14 18:56:04 +02:00
}
2023-10-02 23:10:49 +03:00
public function new(Request $request)
{
2026-01-08 18:50:32 +02:00
if (
!in_array(
$request->input("schema"),
$this->accountService->currentWritableSchemas(),
)
) {
2023-10-17 22:57:25 +03:00
return $this->svelte->render(
layout: "channel",
view: "recordNotFound",
title: "Schema Not Found",
);
}
2026-01-08 18:50:32 +02:00
$schema = $this->channelService->channel->schemas
->where("name", $request->input("schema"))
->first();
$recordHistory = $this->recordManager
->fromSession($request->session())
->getRecords();
2024-08-27 12:24:51 +03:00
$record = $this->recordService->createEmpty($schema);
2023-10-02 23:10:49 +03:00
$queryRecord = QueryRecord::fromRecord($record);
return $this->svelte->render(
layout: "channel",
view: "recordEdit",
title: "New Record",
data: [
"schema" => $schema,
"record" => $queryRecord,
"recordHistory" => $recordHistory,
"isCreateMode" => true,
2026-01-08 18:50:32 +02:00
"isWritable" => in_array(
$record->schema,
$this->accountService->currentWritableSchemas(),
),
],
2023-10-02 23:10:49 +03:00
);
}
2023-10-07 21:18:18 +03:00
public function newInline(Request $request)
{
2026-01-08 18:50:32 +02:00
if (
!in_array(
$request->input("schema"),
$this->accountService->currentWritableSchemas(),
)
) {
2023-10-17 22:57:25 +03:00
return $this->svelte->render(
layout: "channel",
view: "recordNotFound",
title: "Schema Not Found",
);
}
2026-01-08 18:50:32 +02:00
$schema = $this->channelService
->getSchema($request->input("schema"))
->get();
2023-10-07 21:18:18 +03:00
$record = $this->recordService->createEmpty($schema);
$queryRecord = QueryRecord::fromRecord($record);
return [
"schema" => $schema,
"record" => $queryRecord,
"isCreateMode" => true,
2026-01-08 18:50:32 +02:00
"isWritable" => in_array(
$record->schema,
$this->accountService->currentWritableSchemas(),
),
2023-10-07 21:18:18 +03:00
];
}
2023-10-02 23:10:49 +03:00
public function edit(Request $request)
{
$rid = $request->route("rid");
2023-10-04 13:32:30 +03:00
$graph = $this->query
2023-10-02 23:10:49 +03:00
->filter(["id" => $rid])
->limit(1)
->skip(0)
->childrenDepth(2)
2023-11-08 13:55:53 +02:00
->childrenLimit(200)
2023-10-02 23:10:49 +03:00
->parentsDepth(1)
2023-11-08 13:55:53 +02:00
->parentsLimit(200)
2023-10-02 23:10:49 +03:00
->run();
2023-10-04 13:32:30 +03:00
if ($graph->records->isEmpty()) {
2023-10-02 23:10:49 +03:00
return $this->svelte->render(
layout: "channel",
view: "recordNotFound",
title: "Record Not Found",
);
}
2023-10-04 13:32:30 +03:00
$record = $graph->records->first();
2023-10-17 22:57:25 +03:00
2026-01-08 18:50:32 +02:00
if (
!in_array(
$record->schema,
$this->accountService->currentReadableSchemas(),
)
) {
2023-10-17 22:57:25 +03:00
return $this->svelte->render(
layout: "channel",
view: "recordNotFound",
title: "Schema Not Found",
);
}
2023-10-04 13:32:30 +03:00
$schema = $this->channelService->getSchema($record->schema)->get();
2026-01-08 18:50:32 +02:00
$recordHistory = $this->recordManager
->fromSession($request->session())
->push($rid)
->getRecords($rid);
2023-10-02 23:10:49 +03:00
return $this->svelte->render(
layout: "channel",
view: "recordEdit",
title: "Edit Record",
data: [
"schema" => $schema,
2023-10-04 13:32:30 +03:00
"graph" => toArray($graph),
"record" => toArray($record),
2023-10-17 22:57:25 +03:00
"users" => $this->accountService->all(),
2023-10-02 23:10:49 +03:00
"recordHistory" => $recordHistory,
2026-01-08 18:50:32 +02:00
"isWritable" => in_array(
$record->schema,
$this->accountService->currentWritableSchemas(),
),
],
2023-10-02 23:10:49 +03:00
);
}
2023-10-07 21:18:18 +03:00
public function suggestions(Request $request)
{
$arguments = [
"schema" => $request->input("schema"),
];
if ($request->input("value")) {
if (in_array($request->input("ui"), ["text", "date"])) {
2026-01-08 18:50:32 +02:00
$arguments[
"data." . $request->input("field") . "_regex"
] = $request->input("value");
2023-10-07 21:18:18 +03:00
} elseif ($request->input("ui") == "number") {
2026-01-08 18:50:32 +02:00
$arguments[
"data." . $request->input("field") . "_eqnum"
] = floatval($request->input("value"));
2023-10-07 21:18:18 +03:00
} elseif ($request->input("ui") == "date") {
2023-11-17 20:21:45 +02:00
} elseif ($request->input("ui") == "search") {
$arguments["search_regex"] = $request->input("value");
2023-10-07 21:18:18 +03:00
}
}
2026-01-08 18:50:32 +02:00
$records = $this->query->filter($arguments)->limit(10)->tree();
2023-10-07 21:18:18 +03:00
if ($records->isEmpty()) {
return ok([]);
}
return ok($records->toArray());
}
2023-10-02 23:10:49 +03:00
public function save(Request $request)
{
2023-10-07 21:18:18 +03:00
$recordId = $request->input("record.id");
2023-10-02 23:10:49 +03:00
try {
if ($request->input("isCreateMode")) {
2023-10-07 21:18:18 +03:00
$recordId = $this->recordService->create(
2024-08-19 17:48:10 +03:00
data: new RecordInputData(
$request->input("record.schema"),
$recordId ?? "",
$request->input("record.data"),
Status::from($request->input("record.status")),
),
2026-01-08 18:50:32 +02:00
edges: array_map(
EdgeInputData::fromArray(...),
$request->input("edges") ?? [],
),
2023-10-02 23:10:49 +03:00
);
} else {
2024-08-19 17:48:10 +03:00
$this->recordService->updateWithEdges(
2023-10-02 23:10:49 +03:00
id: $request->input("record.id"),
data: $request->input("record.data"),
2024-08-19 17:48:10 +03:00
status: Status::from($request->input("record.status")),
2026-01-08 18:50:32 +02:00
edges: array_map(
EdgeInputData::fromArray(...),
$request->input("edges") ?? [],
),
2023-10-02 23:10:49 +03:00
);
}
2023-10-04 13:32:30 +03:00
$newGraph = $this->query
2023-10-07 21:18:18 +03:00
->filter(["id" => $recordId])
2023-10-02 23:10:49 +03:00
->limit(10)
2023-10-13 21:06:23 +03:00
->childrenDepth(1)
2023-10-02 23:10:49 +03:00
->parentsDepth(1)
->run();
} catch (ValidatorException $th) {
return fail($th->getValidatorErrors());
} catch (LucentException $th) {
return fail($th);
}
2023-10-04 13:32:30 +03:00
return ok(toArray($newGraph));
2023-10-02 23:10:49 +03:00
}
public function clone(Request $request)
{
try {
$newRecordId = $this->recordService->clone(
recordId: $request->route("rid"),
);
} catch (LucentException $th) {
return fail($th);
} catch (ValidatorException $e) {
return fail($e);
}
return ok(["id" => $newRecordId]);
}
public function status(Request $request)
{
$ids = array_map(fn($rec) => $rec["id"], $request->input("records"));
$this->recordService->changeStatusBulk(
status: $request->route("status"),
recordsIds: $ids,
);
return ok();
}
2023-10-06 18:47:50 +03:00
2023-10-16 16:12:21 +03:00
public function emptyTrash(Request $request)
{
2023-10-15 23:40:34 +03:00
$this->recordService->emptyTrash($request->route("schemaName"));
2026-01-08 18:50:32 +02:00
return redirect(
$this->channelService->channel->lucentUrl .
"/content/" .
$request->route("schemaName"),
);
2023-10-15 23:40:34 +03:00
}
2023-10-06 18:47:50 +03:00
public function delete(Request $request)
{
$ids = $request->input("ids");
try {
$this->recordService->deleteMany($ids);
} catch (Throwable $th) {
return fail($th);
}
return ok();
}
2023-10-15 19:14:07 +03:00
public function rollback(Request $request)
{
try {
$this->recordService->rollback(
recordId: $request->route("rid"),
2026-01-08 18:50:32 +02:00
version: (int) $request->route("version"),
2023-10-15 19:14:07 +03:00
);
} catch (ValidatorException $th) {
return fail($th->getFirstValidatorError());
2026-01-08 18:50:32 +02:00
} catch (LucentException | Throwable $th) {
2023-10-15 19:14:07 +03:00
return fail($th);
}
return ok();
}
2023-10-02 23:10:49 +03:00
}