_utb_form_config). Instead of hardcoding HTML, each flow reads this array at render time and generates the form dynamically. This means you can add, reorder, or change fields without touching PHP.
Configuration structure
A form configuration is a flat JSON array. Each element is a field descriptor — an object with atype key that determines what gets rendered and what other properties are valid.
Common field properties
| Property | Type | Description |
|---|---|---|
id | string | Unique identifier within the form. Used for targeting by logic_binding. |
name | string | The HTML name attribute and the key in $_POST. |
type | string | Field type — see the full list below. |
label | string | Visible label text. |
required | boolean | If true, the field is marked required and server-side validation enforces it. |
placeholder | string | Input placeholder text. |
description | string | Helper text rendered below the input. |
css_class | string | Additional CSS class applied to the field wrapper. |
min | number | Minimum character length (minlength attribute). |
max | number | Maximum character length (maxlength attribute). |
pattern | string | HTML pattern attribute for client-side regex validation. |
animation | string | Adds utb-anim-{value} CSS class for entrance animations. |
logic_binding | object | Conditional visibility rule — see Logic binding. |
Field types
text
Single-line text input. Also handles
email, tel, and number subtypes via the type property directly.Single-line input with
type="email". Browser and server-side format validation applied.select
Dropdown with static options. Provide an
options object: { "value": "Label" }.dynamic_select
Dropdown populated at runtime from a registered data source. Use
system_source_id and optional filters — see Dynamic selects.textarea
Multi-line text area.
checkbox
Single checkbox. Value posted is
1 when checked.file
File upload input. Supports
accept (MIME/extension list) and max_size (kilobytes).heading
Non-input section divider with a visible
label. Renders an <h3> heading.notice
Informational callout block with
icon, title, and message (HTML allowed). Supports style: info, warning, success, or error.system_program_selector
Special internal type used by CEPFlow. Renders the program dropdown with price labels and discount banners. Use
system_source_id to override the data source.Static select example
file field example
Logic binding
Thelogic_binding object makes a field conditionally visible based on the current value of another field. The frontend JavaScript reads this at render time and attaches event listeners automatically — no custom JS required.
student_id_field is hidden by default and only shown when the field with id applicant_type has the value STUDENT.
logic_binding properties
logic_binding is processed by the frontend JavaScript (form-builder.js). It controls client-side field visibility.
| Property | Values | Description |
|---|---|---|
action | show | Action to take when the condition is true. The field is hidden otherwise. |
target_field | string | The id of the controlling field. |
operator | equals | Comparison operator used by the frontend JS. |
value | string | The value the controlling field must have for the condition to be true. |
RulesEngine::validate_form()), fields use a conditions array instead:
conditions support these operators: equals, not_equals, contains, greater_than, less_than.
Dynamic selects
Adynamic_select (or system_program_selector) field loads its options from a registered data source managed by DataSourceManager. Data sources are stored in the wp_utb_data_sources database table and can be one of four types:
db_table
Reads rows from a WordPress database table. Configure
table, id_column, label_column, and value_column.api
Fetches and caches options from an external HTTP endpoint. Configure
endpoint, method, response_path, label_field, and value_field.custom_query
Executes a raw SQL query. Use
{{filter:column}} placeholders to inject runtime filter values.taxonomy
Loads terms from any registered WordPress taxonomy, including WooCommerce product attributes (
pa_*).Pricing rules in schema
Pricing logic does not live in the field schema itself — it lives incalculate_dynamic_price() on the flow class. The schema fields supply the values (programme code, academic level, format, etc.) that are stored as cart metadata by prepare_cart_metadata(). Your flow then reads those metadata keys to compute the price.
See Dynamic pricing for full details.
FormConfigManager
UTB\ProductBuilder\Data\FormConfigManager is the class that loads the active configuration for a given product and flow combination. It tries sources in this order:
- Direct product meta —
get_post_meta($product_id, '_utb_form_config', true). If the meta contains valid JSON, it is used as-is. - Linked certificate meta —
get_post_meta($product_id, '_utb_linked_cert_id', true). If a linked certificate hasform_config_jsonin its database row, that is decoded and returned. - Flow default config — Calls
$flow->get_default_config()if the flow implements that method.
Server-side validation with RulesEngine
UTB\ProductBuilder\Logic\RulesEngine provides three public static methods you can call from a flow’s validate_cart_data() or an AJAX handler to validate form data against the field schema.
validate_field(array $field_config, mixed $value, array $context = []): array
Validates a single field value. Returns ['valid' => bool, 'message' => string].
- Required check (if
required: trueand value is empty → fails) - Basic type check (
email,number) - API rule check (if
validation_rule_idis set; calls the configured API viaApiConnectionManager)
validate_form(array $form_config, array $form_data, array $context = []): array
Validates all fields in a form config array at once. Returns ['valid' => bool, 'errors' => array]. Fields that fail the conditions check are skipped automatically.
should_show_field(array $field_config, array $all_values): bool
Returns true if a field should be shown given the current form values. Uses the conditions array on the field descriptor.
Saving a schema programmatically
Save a field array directly to a product’s meta:Complete form configuration example
The following is a representative schema combining most field types and alogic_binding condition: