Files
Reservair/includes/Services/Forms/RsvFormDefinitionValidator.php
T

113 lines
3.8 KiB
PHP
Raw Normal View History

2026-06-14 14:13:37 +02:00
<?php
use Reservair\Templating\RsvTemplateEngine;
/**
* Validates a form definition before it is persisted.
*
* Template checks (symbols, syntax, custom elements) are delegated to the
* common template validator; on top of that this enforces form-level rules,
* such as requiring a submit button once the form defines any fields.
*/
final class RsvFormDefinitionValidator {
/**
* @param array<string,mixed> $definition The inner definition (elements, email_key, success_message).
* @return list<string> Human-readable problems; empty when the definition is valid.
*/
public function validate(array $definition): array {
$elements = is_array($definition['elements'] ?? null) ? $definition['elements'] : [];
$symbols = $this->symbols($elements);
$engine = $this->engine();
$errors = [];
// Templates reference submitted values by form-element name.
foreach ($this->templates($definition, $elements) as $label => $template) {
foreach ($engine->validate($template, $symbols) as $problem) {
$errors[] = "{$label}: {$problem}";
}
}
// A form that collects fields must give the visitor a way to send them.
if ($elements !== [] && !$this->has_submit($elements)) {
$errors[] = 'Form must contain a submit button.';
}
return $errors;
}
/**
* Names that templates may reference — the form's symbol table.
*
* @param array<int,mixed> $elements
* @return list<string>
*/
private function symbols(array $elements): array {
2026-06-16 19:33:55 +02:00
$names = RsvFormCalculatedValues::names(); // calculated values are referencable too
2026-06-14 14:13:37 +02:00
foreach ($elements as $el) {
$name = is_array($el) ? ($el['name'] ?? '') : '';
if (is_string($name) && $name !== '') {
$names[] = $name;
}
}
return $names;
}
/**
* The definition's admin-authored templates, keyed by a label used to
* prefix any problems found in them.
*
* @param array<string,mixed> $definition
* @param array<int,mixed> $elements
* @return array<string,string>
*/
private function templates(array $definition, array $elements): array {
$templates = [];
$success = $definition['success_message'] ?? '';
if (is_string($success) && trim($success) !== '') {
$templates['Success message'] = $success;
}
foreach ($elements as $el) {
if (!is_array($el) || ($el['type'] ?? '') !== 'reservation') {
continue;
}
$email_templates = $el['email_templates'] ?? [];
if (!is_array($email_templates)) {
continue;
}
foreach (['on_accepted' => 'accepted', 'on_refused' => 'refused'] as $key => $human) {
$tpl = $email_templates[$key] ?? [];
if (!is_array($tpl)) {
continue;
}
foreach (['subject', 'body'] as $part) {
$value = $tpl[$part] ?? '';
if (is_string($value) && trim($value) !== '') {
$templates["Email ({$human} {$part})"] = $value;
}
}
}
}
return $templates;
}
/** @param array<int,mixed> $elements */
private function has_submit(array $elements): bool {
foreach ($elements as $el) {
if (is_array($el) && ($el['type'] ?? '') === 'button') {
return true;
}
}
return false;
}
private function engine(): RsvTemplateEngine {
global $rsv_template_registry;
return new RsvTemplateEngine(registry: $rsv_template_registry);
}
}