Files
lucent-laravel/src/Core/Record/RecordValidationModule.php
T
2026-01-13 17:51:19 +02:00

269 lines
7.1 KiB
PHP

<?php namespace Lucent\Core\Record;
use Illuminate\Support\Facades\Validator;
use Lucent\Core\Data\RecordError;
use Lucent\Core\Data\RecordField;
use Lucent\Core\Data\RecordMode;
use Lucent\Core\Schema\Data\Field;
class RecordValidationModule
{
/**
* Validates a record against a schema.
*
* @param string[] $locales
* @param Field[] $schemaFields
* @param RecordField[] $recordFields
* @param Edge[] $recordEdges
* @return RecordError[]
*/
public static function validate(
array $locales,
array $schemaFields,
array $recordFields,
array $recordEdges,
): array {
$errors = [];
foreach ($schemaFields as $schemaField) {
$res = static::validateField(
"main",
$schemaField,
$recordFields,
$recordEdges,
);
$errors[] = $res;
if ($schemaField->translatable) {
foreach ($locales as $locale) {
$res = static::validateField(
$locale["id"],
$schemaField,
$recordFields,
$recordEdges,
);
$errors[] = $res;
}
}
}
return collect($errors)
->filter(fn($err) => $err !== null)
->values()
->toArray();
}
/**
*
* @param Field $schemaField
* @param RecordField[] $recordFields
* @param Edge[] $recordEdges
* @return array
*/
public static function validateField(
string $locale,
Field $schemaField,
array $recordFields,
array $recordEdges,
): ?RecordError {
// General Validations
$error = static::validateRequired(
$locale,
$schemaField,
$recordFields,
$recordEdges,
);
if (!empty($error)) {
return $error;
}
// Type specific
$error = match ($schemaField->type) {
"text" => static::validateText(
$locale,
$schemaField,
$recordFields,
),
"relation" => static::validateRelation(
$locale,
$schemaField,
$recordEdges,
),
default => static::validateText(
$locale,
$schemaField,
$recordFields,
),
};
return $error;
}
/**
*
* @param Field $schemaField
* @param RecordField[] $recordFields
* @param Edge[] $recordEdges
* @return ?RecordError
*/
public static function validateRequired(
string $locale,
Field $schemaField,
array $recordFields,
array $recordEdges,
): ?RecordError {
if ($schemaField->required === false) {
return null;
}
return match ($schemaField->type) {
"relation" => static::validateRequiredRelation(
$locale,
$schemaField,
$recordEdges,
),
default => static::validateRequiredField(
$locale,
$schemaField,
$recordFields,
),
};
}
private static function validateRequiredField(
string $locale,
Field $schemaField,
array $recordFields,
): ?RecordError {
$recordField = static::findField($recordFields, $schemaField, $locale);
if (empty($recordField) || empty($recordField->value)) {
return new RecordError(
$schemaField->id,
$locale,
"This field is required",
);
}
return null;
}
private static function validateRequiredRelation(
string $locale,
Field $schemaField,
array $recordEdges,
): ?RecordError {
if (
collect($recordEdges)
->where("fieldId", $schemaField->id)
->where("locale", $locale)
->where("mode", RecordMode::DRAFT)
->isEmpty()
) {
return new RecordError(
$schemaField->id,
$locale,
"This field is required",
);
}
return null;
}
/**
*
* @param Field $schemaField
* @param RecordField[] $recordFields
* @return ?RecordError
*/
public static function validateText(
string $locale,
Field $schemaField,
array $recordFields,
): ?RecordError {
$recordField = static::findField($recordFields, $schemaField, $locale);
if (empty($recordField)) {
// We have already checked for required fields so only validate existing
return null;
}
$rules = "";
if ($schemaField->props->min > 0) {
$rules = "min:" . $schemaField->props->min;
}
if ($schemaField->props->max > 0) {
$rules = "max:" . $schemaField->props->max;
}
if (empty($rules)) {
return null;
}
$validator = Validator::make(
["value" => $recordField->value],
[
"value" => $rules,
],
);
if ($validator->fails()) {
return new RecordError(
$schemaField->id,
$locale,
$validator->errors()->first(),
);
}
return null;
}
/**
*
* @param Field $schemaField
* @param Edge[] $recordEdges
* @return ?RecordError
*/
public static function validateRelation(
string $locale,
Field $schemaField,
array $recordEdges,
): ?RecordError {
$count = collect($recordEdges)
->where("fieldId", $schemaField->id)
->where("locale", $locale)
->where("mode", RecordMode::DRAFT)
->count();
if ($schemaField->props->min > 0) {
if ($count < $schemaField->props->min) {
return new RecordError(
$schemaField->id,
$locale,
"You have to have at least {$schemaField->props->min} related records",
);
}
}
if ($schemaField->props->max > 0) {
if ($count > $schemaField->props->max) {
return new RecordError(
$schemaField->id,
$locale,
"You have to have at most {$schemaField->props->max} related records",
);
}
}
return null;
}
private static function findField(
array $recordFields,
Field $schemaField,
string $locale,
): ?RecordField {
return collect($recordFields)->first(
fn(RecordField $field) => $field->fieldId === $schemaField->id &&
$field->locale === $locale &&
$field->mode === RecordMode::DRAFT,
);
}
}