@@ -0,0 +1,112 @@
|
||||
<?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 {
|
||||
$names = [];
|
||||
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);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user