91 lines
3.7 KiB
PHP
91 lines
3.7 KiB
PHP
<?php
|
|
|
|
namespace App\Services;
|
|
|
|
use Illuminate\Support\Facades\Validator;
|
|
use Illuminate\Validation\ValidationException;
|
|
|
|
/** First draft adapter, not a complete contract validator or a publication gate. */
|
|
class OpportunityRealEstateDraft
|
|
{
|
|
/**
|
|
* @param array<string,mixed> $values
|
|
* @return array<string,mixed>
|
|
*/
|
|
public function validate(array $values): array
|
|
{
|
|
abort_unless(config('portal.opportunity_specialized_drafts_enabled'), 422, 'Campos especializados ainda não disponibilizados neste ambiente.');
|
|
$bytes = file_get_contents(resource_path('data/investmoz-opportunity-modular.schema.json'));
|
|
if ($bytes === false) {
|
|
throw new \RuntimeException('Normative opportunity schema unavailable.');
|
|
}
|
|
$schema = json_decode($bytes, true, 512, JSON_THROW_ON_ERROR);
|
|
$properties = $schema['$defs']['realEstatePatch']['properties'];
|
|
if ($values === [] || array_diff(array_keys($values), array_keys($properties))) {
|
|
throw ValidationException::withMessages(['real_estate' => 'Indique campos do contrato imobiliário; campos desconhecidos ou de resposta não são graváveis.']);
|
|
}
|
|
$errors = [];
|
|
foreach ($values as $key => $value) {
|
|
$this->check($value, $properties[$key], 'real_estate.'.$key, $errors);
|
|
}
|
|
if ($errors) {
|
|
throw ValidationException::withMessages($errors);
|
|
}
|
|
|
|
return $values;
|
|
}
|
|
|
|
/**
|
|
* @param array<string,mixed> $schema
|
|
* @param array<string,string> $errors
|
|
*/
|
|
private function check(mixed $value, array $schema, string $path, array &$errors): void
|
|
{
|
|
$types = (array) ($schema['type'] ?? []);
|
|
$valid = $types === [] || array_filter($types, fn ($type) => match ($type) {
|
|
'null' => $value === null,
|
|
'string' => is_string($value),
|
|
'integer' => is_int($value),
|
|
'number' => is_int($value) || is_float($value),
|
|
'boolean' => is_bool($value),
|
|
'array' => is_array($value) && array_is_list($value),
|
|
// Laravel request arrays represent JSON objects, including empty ones.
|
|
'object' => is_array($value) && ($value === [] || ! array_is_list($value)),
|
|
default => false,
|
|
}) !== [];
|
|
if (! $valid || (isset($schema['enum']) && ! in_array($value, $schema['enum'], true))) {
|
|
$errors[$path] = 'Tipo ou valor fora do contrato imobiliário §26.';
|
|
|
|
return;
|
|
}
|
|
if ($value === null) {
|
|
return;
|
|
}
|
|
$rules = [];
|
|
foreach (['minLength' => 'min', 'maxLength' => 'max', 'minItems' => 'min'] as $keyword => $rule) {
|
|
if (isset($schema[$keyword])) {
|
|
$rules[] = $rule.':'.$schema[$keyword];
|
|
}
|
|
}
|
|
if (in_array('number', $types, true) || in_array('integer', $types, true)) {
|
|
$rules[] = 'numeric';
|
|
foreach (['minimum' => 'min', 'maximum' => 'max', 'exclusiveMinimum' => 'gt'] as $keyword => $rule) {
|
|
if (isset($schema[$keyword])) {
|
|
$rules[] = $rule.':'.$schema[$keyword];
|
|
}
|
|
}
|
|
}
|
|
if (($schema['format'] ?? null) === 'uuid') {
|
|
$rules[] = 'uuid';
|
|
}
|
|
if ($rules && Validator::make(['value' => $value], ['value' => $rules])->fails()) {
|
|
$errors[$path] = 'Valor fora dos limites/formato do contrato imobiliário §26.';
|
|
}
|
|
if (isset($schema['items']) && is_array($value)) {
|
|
foreach ($value as $index => $item) {
|
|
$this->check($item, $schema['items'], $path.'.'.$index, $errors);
|
|
}
|
|
}
|
|
}
|
|
}
|