191 lines
6.2 KiB
PHP
Executable File
191 lines
6.2 KiB
PHP
Executable File
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace Evomedien\Vitec\Forms;
|
|
|
|
/**
|
|
* Single source of truth for the VITEC form plugins.
|
|
*
|
|
* The SAME definition drives (a) the JSON the React frontend renders the
|
|
* form from and (b) the server-side validation of submissions in
|
|
* FormSubmissionMiddleware. Field names are camelCase (React contract);
|
|
* mapping to Salesforce field ids happens inside the Salesforce delivery.
|
|
*
|
|
* Field spec: name, type (text|email|tel|select|textarea), label, required,
|
|
* options (select only) OR optionsSource (frontend-supplied list, e.g. the
|
|
* ISO country list).
|
|
*/
|
|
final class FormDefinitions
|
|
{
|
|
public const HONEYPOT_FIELD = '_website';
|
|
|
|
/** CType => form key */
|
|
public const CTYPE_MAP = [
|
|
'vitec_contactform' => 'contact',
|
|
'vitec_demoform' => 'demo',
|
|
'vitec_helpdeskform' => 'helpdesk',
|
|
];
|
|
|
|
private const SOLUTION_OPTIONS = [
|
|
'IPTV Distribution',
|
|
'Digital Signage',
|
|
'Video Streaming',
|
|
'Video Contribution',
|
|
'Situational Awareness and ISR',
|
|
'Remote Production',
|
|
'Encoders & Decoders',
|
|
'Custom Design Service',
|
|
];
|
|
|
|
/**
|
|
* @return array<string,mixed>|null
|
|
*/
|
|
public static function get(string $formKey): ?array
|
|
{
|
|
$all = self::all();
|
|
return $all[$formKey] ?? null;
|
|
}
|
|
|
|
/**
|
|
* @return array<string,array<string,mixed>>
|
|
*/
|
|
public static function all(): array
|
|
{
|
|
$contactFields = [
|
|
self::text('firstName', 'First Name', true),
|
|
self::text('lastName', 'Last Name', true),
|
|
self::text('company', 'Company', true),
|
|
self::field('email', 'email', 'Email', true),
|
|
self::field('phone', 'tel', 'Phone', true),
|
|
self::text('street', 'Street', false),
|
|
self::text('city', 'City', false),
|
|
self::text('zip', 'Zip / Postal Code', false),
|
|
self::countrySelect(),
|
|
self::select('state', 'State', false, [], 'usStates'),
|
|
self::select('solution', 'Solution of Interest', false, self::SOLUTION_OPTIONS),
|
|
self::field('message', 'textarea', 'Message', false),
|
|
];
|
|
|
|
return [
|
|
'contact' => [
|
|
'formKey' => 'contact',
|
|
'title' => 'Contact VITEC',
|
|
'fields' => $contactFields,
|
|
],
|
|
'demo' => [
|
|
'formKey' => 'demo',
|
|
'title' => 'Request a Demo',
|
|
'fields' => $contactFields,
|
|
],
|
|
'helpdesk' => [
|
|
'formKey' => 'helpdesk',
|
|
'title' => 'Request Access to Online HelpDesk',
|
|
'fields' => [
|
|
self::text('firstName', 'First Name', true),
|
|
self::text('lastName', 'Last Name', true),
|
|
self::text('company', 'Company', true),
|
|
self::field('phone', 'tel', 'Phone Number', false),
|
|
self::field('email', 'email', 'Email Address', true),
|
|
self::countrySelect(true),
|
|
self::text('product', 'Product', false),
|
|
self::text('serialNumber', 'Serial Number', false),
|
|
],
|
|
],
|
|
];
|
|
}
|
|
|
|
/**
|
|
* Validate a submission payload against the definition.
|
|
*
|
|
* @param array<string,mixed> $payload
|
|
* @return array<string,string> field => error (empty = valid)
|
|
*/
|
|
public static function validate(string $formKey, array $payload): array
|
|
{
|
|
$definition = self::get($formKey);
|
|
if ($definition === null) {
|
|
return ['_form' => 'Unknown form'];
|
|
}
|
|
|
|
$errors = [];
|
|
foreach ($definition['fields'] as $field) {
|
|
$name = $field['name'];
|
|
$value = trim((string)($payload[$name] ?? ''));
|
|
|
|
if (($field['required'] ?? false) && $value === '') {
|
|
$errors[$name] = 'This field is required.';
|
|
continue;
|
|
}
|
|
if ($value === '') {
|
|
continue;
|
|
}
|
|
if ($field['type'] === 'email' && !filter_var($value, FILTER_VALIDATE_EMAIL)) {
|
|
$errors[$name] = 'Please enter a valid email address.';
|
|
}
|
|
if (mb_strlen($value) > 5000) {
|
|
$errors[$name] = 'Value is too long.';
|
|
}
|
|
}
|
|
|
|
return $errors;
|
|
}
|
|
|
|
/**
|
|
* Reduce a raw payload to the defined fields (drops everything unknown).
|
|
*
|
|
* @param array<string,mixed> $payload
|
|
* @return array<string,string>
|
|
*/
|
|
public static function filterPayload(string $formKey, array $payload): array
|
|
{
|
|
$definition = self::get($formKey);
|
|
if ($definition === null) {
|
|
return [];
|
|
}
|
|
$out = [];
|
|
foreach ($definition['fields'] as $field) {
|
|
$out[$field['name']] = trim((string)($payload[$field['name']] ?? ''));
|
|
}
|
|
return $out;
|
|
}
|
|
|
|
// ------------------------------------------------------------ field DSL
|
|
|
|
/** @return array<string,mixed> */
|
|
private static function field(string $name, string $type, string $label, bool $required): array
|
|
{
|
|
return ['name' => $name, 'type' => $type, 'label' => $label, 'required' => $required];
|
|
}
|
|
|
|
/** @return array<string,mixed> */
|
|
private static function text(string $name, string $label, bool $required): array
|
|
{
|
|
return self::field($name, 'text', $label, $required);
|
|
}
|
|
|
|
/**
|
|
* @param array<int,string> $options
|
|
* @return array<string,mixed>
|
|
*/
|
|
private static function select(string $name, string $label, bool $required, array $options, string $optionsSource = ''): array
|
|
{
|
|
$field = self::field($name, 'select', $label, $required);
|
|
if ($options !== []) {
|
|
$field['options'] = $options;
|
|
}
|
|
if ($optionsSource !== '') {
|
|
// The frontend supplies this list (e.g. ISO countries) — keeps
|
|
// the definition lean and the lists consistent app-wide.
|
|
$field['optionsSource'] = $optionsSource;
|
|
}
|
|
return $field;
|
|
}
|
|
|
|
/** @return array<string,mixed> */
|
|
private static function countrySelect(bool $required = false): array
|
|
{
|
|
return self::select('country', 'Country', $required, [], 'countries');
|
|
}
|
|
}
|