2023-10-02 23:10:49 +03:00
|
|
|
<?php
|
|
|
|
|
|
|
|
|
|
namespace Lucent\Record;
|
|
|
|
|
|
|
|
|
|
use Illuminate\Support\Str;
|
|
|
|
|
use Lucent\Account\AuthService;
|
|
|
|
|
use Lucent\Channel\ChannelService;
|
|
|
|
|
use Lucent\Edge\Edge;
|
|
|
|
|
use Lucent\Edge\EdgeCollection;
|
|
|
|
|
use Lucent\Edge\EdgeRepo;
|
|
|
|
|
use Lucent\File\FileService;
|
|
|
|
|
use Lucent\Id\Id;
|
|
|
|
|
use Lucent\LucentException;
|
|
|
|
|
use Lucent\Query\Query;
|
|
|
|
|
use Lucent\Revision\RevisionService;
|
|
|
|
|
use Lucent\Schema\FieldInterface;
|
|
|
|
|
use Lucent\Schema\Schema;
|
|
|
|
|
use Lucent\Schema\Validator\Validator;
|
|
|
|
|
use Lucent\Schema\Validator\ValidatorException;
|
|
|
|
|
|
|
|
|
|
readonly class RecordService
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
public function __construct(
|
|
|
|
|
private AuthService $authService,
|
|
|
|
|
private RevisionService $revisionService,
|
|
|
|
|
private ChannelService $channelService,
|
|
|
|
|
private Validator $recordValidator,
|
|
|
|
|
private Query $query,
|
2023-10-06 18:47:50 +03:00
|
|
|
private InputFormatter $inputFormatter,
|
|
|
|
|
private RecordRepo $recordRepo
|
2023-10-02 23:10:49 +03:00
|
|
|
)
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @throws LucentException
|
|
|
|
|
* @throws ValidatorException
|
|
|
|
|
*/
|
|
|
|
|
public
|
|
|
|
|
function create(
|
|
|
|
|
string $schemaName,
|
|
|
|
|
array $data,
|
|
|
|
|
?string $id = null,
|
|
|
|
|
array $file = [],
|
|
|
|
|
array $edges = [],
|
|
|
|
|
string $status = "draft",
|
|
|
|
|
string $uploadFromUrl = "",
|
|
|
|
|
): string
|
|
|
|
|
{
|
|
|
|
|
$schema = $this->channelService->channel->schemas->where("name", $schemaName)->first();
|
|
|
|
|
|
|
|
|
|
if (empty($schema)) {
|
|
|
|
|
throw new LucentException("The schema " . $schemaName . " does not exist");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$formattedData = $this->inputFormatter->fill($schemaName, new RecordData($data));
|
|
|
|
|
if (empty($formattedData["id"])) {
|
|
|
|
|
$formattedData["id"] = Id::new();
|
|
|
|
|
}
|
|
|
|
|
$uploadResult = FileService::create($schema, $uploadFromUrl, $file);
|
|
|
|
|
|
|
|
|
|
$uniqueEdges = collect($edges)
|
|
|
|
|
->map(function ($edge, $index) {
|
2023-10-04 13:32:30 +03:00
|
|
|
$edge["rank"] = $index;
|
|
|
|
|
return (array)(new Edge(...$edge));
|
2023-10-02 23:10:49 +03:00
|
|
|
})
|
|
|
|
|
->unique(fn($e) => $e['field'] . $e['source'] . $e['target'] . $e['sourceSchema'])
|
|
|
|
|
->values()->toArray();
|
|
|
|
|
$uniqueEdgesCollection = EdgeCollection::fromArray($uniqueEdges);
|
|
|
|
|
|
|
|
|
|
if ($uploadResult->isDuplicate) {
|
|
|
|
|
EdgeRepo::update($uploadResult->duplicateId, $uniqueEdgesCollection);
|
|
|
|
|
return $uploadResult->duplicateId;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$record = new Record(
|
2023-10-07 21:18:18 +03:00
|
|
|
id: empty($id) ? Id::new() : $id,
|
2023-10-04 13:32:30 +03:00
|
|
|
schema: $schema->name,
|
|
|
|
|
status: Status::from($status),
|
|
|
|
|
_sys: System::newRecord($this->authService->currentUserId()),
|
2023-10-02 23:10:49 +03:00
|
|
|
data: $formattedData,
|
|
|
|
|
_file: $uploadResult->recordFile,
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
$errors = $this->recordValidator->check($schemaName, $record->data, $uniqueEdgesCollection);
|
|
|
|
|
if ($errors->isNotEmpty()) {
|
|
|
|
|
$this->recordValidator->throwException($errors);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
RecordRepo::create($record);
|
|
|
|
|
EdgeRepo::update($record->id, $uniqueEdgesCollection);
|
|
|
|
|
$this->revisionService->create($record);
|
|
|
|
|
return $record->id;
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @throws LucentException
|
|
|
|
|
* @throws ValidatorException
|
|
|
|
|
*/
|
|
|
|
|
public
|
|
|
|
|
function update(
|
|
|
|
|
string $id,
|
|
|
|
|
array $data,
|
|
|
|
|
string $status = "draft",
|
|
|
|
|
array $edges = [],
|
|
|
|
|
bool $updateEdges = false
|
|
|
|
|
): void
|
|
|
|
|
{
|
|
|
|
|
|
2023-10-04 13:32:30 +03:00
|
|
|
$record = $this->query->filter(["id" => $id])->run()->records->first();
|
2023-10-02 23:10:49 +03:00
|
|
|
|
|
|
|
|
if (empty($record)) {
|
|
|
|
|
throw new LucentException("Record id is missing");
|
|
|
|
|
}
|
2023-10-04 13:32:30 +03:00
|
|
|
$formattedData = $this->inputFormatter->fill($record->schema, new RecordData($data));
|
2023-10-02 23:10:49 +03:00
|
|
|
|
|
|
|
|
if ($updateEdges) {
|
|
|
|
|
$uniqueEdges = collect($edges)
|
|
|
|
|
->map(function ($edge, $index) {
|
2023-10-04 13:32:30 +03:00
|
|
|
$edge["rank"] = $index;
|
2023-10-02 23:10:49 +03:00
|
|
|
$edgeData = (array)(new Edge(...$edge));
|
|
|
|
|
return $edgeData;
|
|
|
|
|
})
|
|
|
|
|
->unique(fn($e) => $e['field'] . $e['source'] . $e['target'] . $e['sourceSchema'])
|
|
|
|
|
->values()->toArray();
|
|
|
|
|
$uniqueEdgesCollection = EdgeCollection::fromArray($uniqueEdges);
|
2023-10-04 13:32:30 +03:00
|
|
|
$errors = $this->recordValidator->check($record->schema, $formattedData, $uniqueEdgesCollection);
|
2023-10-02 23:10:49 +03:00
|
|
|
} else {
|
2023-10-04 13:32:30 +03:00
|
|
|
$errors = $this->recordValidator->check($record->schema, $formattedData, null);
|
2023-10-02 23:10:49 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
$newRecord = new Record(
|
|
|
|
|
id: $record->id,
|
2023-10-04 13:32:30 +03:00
|
|
|
schema: $record->schema,
|
|
|
|
|
status: Status::from($status),
|
|
|
|
|
_sys: $record->_sys->update($this->authService->currentUserId()),
|
2023-10-02 23:10:49 +03:00
|
|
|
data: $record->data->merge($formattedData),
|
|
|
|
|
_file: $record->_file,
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if ($errors->isNotEmpty()) {
|
|
|
|
|
$this->recordValidator->throwException($errors);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
RecordRepo::update($newRecord);
|
|
|
|
|
if ($updateEdges) {
|
|
|
|
|
EdgeRepo::update($newRecord->id, $uniqueEdgesCollection);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$this->revisionService->create($newRecord);
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
*/
|
|
|
|
|
public
|
|
|
|
|
function changeStatusBulk(
|
|
|
|
|
string $status,
|
|
|
|
|
array $recordsIds,
|
|
|
|
|
): void
|
|
|
|
|
{
|
2023-10-04 13:32:30 +03:00
|
|
|
RecordRepo::updateStatusBulk(Status::from($status), $recordsIds);
|
2023-10-02 23:10:49 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @throws LucentException
|
|
|
|
|
* @throws ValidatorException
|
|
|
|
|
*/
|
|
|
|
|
public
|
|
|
|
|
function clone(
|
|
|
|
|
string $recordId,
|
|
|
|
|
): string
|
|
|
|
|
{
|
2023-10-04 13:32:30 +03:00
|
|
|
$graph = $this->query
|
2023-10-02 23:10:49 +03:00
|
|
|
->filter(["id" => $recordId])
|
|
|
|
|
->limit(1)
|
|
|
|
|
->childrenDepth(1)
|
2023-10-04 13:32:30 +03:00
|
|
|
->run();
|
2023-10-02 23:10:49 +03:00
|
|
|
|
2023-10-04 13:32:30 +03:00
|
|
|
$record = $graph->records->first();
|
2023-10-02 23:10:49 +03:00
|
|
|
if (empty($record)) {
|
|
|
|
|
throw new LucentException("Record id is missing");
|
|
|
|
|
}
|
2023-10-04 13:32:30 +03:00
|
|
|
|
2023-10-02 23:10:49 +03:00
|
|
|
$newRecordId = (string)Str::uuid();
|
|
|
|
|
$newEdgesData = $graph->edges
|
|
|
|
|
->filter(fn(Edge $edge) => $edge->source == $recordId)
|
|
|
|
|
->values()
|
|
|
|
|
->map(function (Edge $edge) use ($newRecordId) {
|
|
|
|
|
$edge->source = $newRecordId;
|
|
|
|
|
return $edge->toArray();
|
|
|
|
|
})->toArray();
|
|
|
|
|
|
|
|
|
|
$record->id = $newRecordId;
|
|
|
|
|
|
|
|
|
|
return $this->create(
|
2023-10-04 13:32:30 +03:00
|
|
|
schemaName: $record->schema,
|
2023-10-02 23:10:49 +03:00
|
|
|
data: $record->data->toArray(),
|
|
|
|
|
id: $record->id,
|
|
|
|
|
file: $record->_file?->toArray() ?? [],
|
|
|
|
|
edges: $newEdgesData,
|
|
|
|
|
status: "draft"
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public function deleteMany(
|
|
|
|
|
array $recordsIds,
|
|
|
|
|
): void
|
|
|
|
|
{
|
2023-10-06 18:47:50 +03:00
|
|
|
$this->recordRepo->deleteMany($recordsIds);
|
2023-10-02 23:10:49 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @throws LucentException
|
|
|
|
|
* @throws ValidatorException
|
|
|
|
|
*/
|
|
|
|
|
public function rollback(
|
|
|
|
|
string $recordId,
|
|
|
|
|
int $version,
|
|
|
|
|
): void
|
|
|
|
|
{
|
|
|
|
|
$revision = $this->revisionService->getByRecordIdAndVersion($recordId, $version)->get();
|
|
|
|
|
$this->update(
|
|
|
|
|
id: $revision->recordId,
|
|
|
|
|
data: $revision->data->toArray(),
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public function createEmpty(
|
|
|
|
|
Schema $schema,
|
|
|
|
|
string $userId,
|
|
|
|
|
): Record
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
$defaultValues = $schema->fields->reduce(function ($carry, FieldInterface $f) {
|
|
|
|
|
$carry[$f->name] = $f->default ?? null;
|
|
|
|
|
return $carry;
|
|
|
|
|
}, []);
|
|
|
|
|
|
|
|
|
|
$formattedData = $this->inputFormatter->fill($schema->name, new RecordData($defaultValues));
|
|
|
|
|
|
|
|
|
|
return new Record(
|
|
|
|
|
id: Id::new(),
|
2023-10-04 13:32:30 +03:00
|
|
|
schema: $schema->name,
|
|
|
|
|
status: Status::DRAFT,
|
|
|
|
|
_sys: System::newRecord($userId),
|
2023-10-02 23:10:49 +03:00
|
|
|
data: $formattedData,
|
|
|
|
|
_file: null,
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
}
|