Field API Reference

Section: Developer Guide

Topic: Fields

Applies to: Elzo Forms Free and Pro

Scope: PHP field classes, field configuration, admin save normalization, frontend rendering, validation, sanitization, JSON form data

This reference documents the \ElzoForms\Field\Field base class and the data contract used by Elzo Forms field types.

If you are building your first custom field, start with Build a Custom Field. Use this page when you need the complete method map, standard keys, normalization behavior, JSON shape, and hooks.


Field lifecycle

A field usually moves through these stages:

  1. A raw field configuration array comes from the WordPress admin builder or stored JSON.
  2. Elzo Forms creates a field object with \ElzoForms\Field\Field::from() or a concrete field class constructor.
  3. The constructor merges incoming data with get_defaults().
  4. If the field has no id, Elzo Forms generates one.
  5. The field class runs normalize_configuration().
  6. Frontend rendering calls get_data(), then loads a template or falls back to render_html().
  7. Submission handling calls validate() and sanitize() for submitted values.

Admin save has one extra step: after generic sanitization, Elzo Forms calls Field::normalize_definition() for each field before saving the form data.


Registering field types

Custom field types are normally registered with elzo_forms_field_types.

add_filter( 'elzo_forms_field_types', function( array $types ): array {
    $types['star_rating'] = [
        'label' => __( 'Star Rating', 'my-elzo-fields' ),
        'class' => \MyElzoFields\Field_Star_Rating::class,
    ];

    return $types;
} );

The field type key is the value stored in JSON as "type". The class should extend \ElzoForms\Field\Field and implement validate() and sanitize().

You can also register a class programmatically with Field::register_field_type( $type, $class_name ), but the filter is the usual integration point because it feeds the admin UI and the factory mapping.


Configuration normalization

Field configuration is the data that defines a field: label, type, options, display settings, conditional logic, and custom settings. It is different from submitted frontend values.

Use normalize_configuration() when a field-specific setting needs a reliable stored shape.

protected function normalize_configuration( array $data ): array {
    $max_rating = isset( $data['max_rating'] ) ? absint( $data['max_rating'] ) : 5;

    if ( $max_rating < 3 ) {
        $max_rating = 3;
    }

    if ( $max_rating > 10 ) {
        $max_rating = 10;
    }

    $data['max_rating'] = $max_rating;

    return $data;
}

Normalization should be:

  • idempotent: running it twice should produce the same result;
  • side-effect free: no output, database writes, remote requests, or file writes;
  • configuration-only: do not use it to sanitize submitted values;
  • conservative: preserve unknown keys unless the field explicitly forbids them;
  • forgiving: prefer safe fallback values over throwing for malformed configuration.

The built-in Range field uses this contract to normalize min_value, max_value, and default_value. For example, if a two-handle range default is outside the configured bounds, it is clamped before the field definition is stored.


Admin save order

When the admin builder saves a form, field data is processed in this order:

  1. Template rows are removed from the posted payload.
  2. Known nested structures are sanitized, including options, rules, width, content, and allowed_file_types.
  3. Checkbox-like flags are normalized to booleans: required, logic, primary_field, and submission_table_field.
  4. Field::normalize_definition() creates the appropriate field class and runs normalize_configuration().
  5. elzo_forms_field_before_save can adjust the normalized field array.
  6. elzo_forms_step_before_save can adjust the step.
  7. After all fields are collected, field_key values are generated, slugified, limited to 64 characters, and made unique across the whole form.

Because field_key is finalized after field normalization, custom field classes should not rely on final field-key uniqueness inside normalize_configuration().


Standard configuration keys

Key Type Purpose
id int|string Field identifier. Generated when missing.
type string Field type key, such as text, select, or a custom type.
field_key string Machine-readable key for integrations and internal references. Auto-generated and unique per form.
label string Frontend label.
placeholder string Input placeholder where the field type supports it.
admin_label string Admin-only display label. Used before label or placeholder in admin titles.
under_label string Help text displayed under the label.
under_field string Help text displayed under the input.
options array Selectable options for select, checkbox, radio, and similar fields.
required bool Whether the field must receive a value. Read-only fields may override this.
default_value mixed Default value used when no submitted or runtime value is present.
value mixed Runtime value. Usually supplied during rendering or submission handling, not stored as the default configuration.
step_index int Index of the parent form step.
index int Index of the field inside its step.
width array Responsive column width map keyed by breakpoint, for example xs, md, lg.
custom_id string Custom HTML ID for the input/control.
custom_class string Additional CSS class for the input/control.
wrapper_custom_id string Custom HTML ID for the field wrapper.
wrapper_custom_class string Additional CSS class for the field wrapper.
logic bool Enables conditional logic for the field.
rules array Conditional logic groups and rule items.
primary_field bool Marks the field as useful for identifying a submission.
submission_table_field bool Shows the field in the admin submissions table.

Options format

Option-based fields use an array of objects with value, label, and optional description.

[
  {
    "value": "basic",
    "label": "Basic",
    "description": "For small forms"
  },
  {
    "value": "pro",
    "label": "Pro",
    "description": "For advanced workflows"
  }
]

In the admin builder, options are entered one per line. The supported shorthand is:

value : Label || Description

Conditional logic format

Conditional logic is stored as groups. Groups are OR conditions; rules inside a group are AND conditions.

{
  "logic": true,
  "rules": [
    [
      {
        "type": "field",
        "operator": "==",
        "settings": {
          "field_id": "101",
          "value": "yes"
        }
      }
    ],
    [
      {
        "type": "cookie",
        "operator": "exists",
        "settings": {
          "name": "campaign"
        }
      }
    ]
  ]
}

Field classes can shape the conditional logic UI with get_logic_operators(), get_logic_value_options(), and get_logic_value_source().


Built-in field-specific keys

Type Common keys
text subtype, placeholder, default_value, input_prepend, input_append
textarea placeholder, default_value, rows_amount, max_length
select options, placeholder, default_value, multiple, search
checkbox options, default_value, layout, style, min_selections, max_selections
radio options, default_value, layout
range range_type, min_value, max_value, step, default_value
file allowed_file_types, multiple, min_files, max_files, max_file_size
hidden default_value
content content, is_html
button button_type, button_title, button_url, button_target

Custom field types may add their own keys. Add defaults in get_defaults(), normalize them in normalize_configuration(), and document their expected JSON shape.


Core Field methods

Method Visibility Purpose
from( $field ) static public Factory that creates a typed field instance from an array or numeric ID. Caches instances by field ID.
get_registered_field_types() static protected Returns the field type to class map from admin utilities and registered filters.
register_field_type( $type, $class_name ) static public Registers a field class in the local factory map.
__construct( $field ) public Merges defaults, generates an ID if needed, then normalizes configuration.
normalize_configuration( $data ) protected Normalizes field configuration from admin payloads or JSON. Override for custom settings.
normalize_definition( $field ) static public Normalizes a raw field array through its registered field class and returns the array form.
get_defaults() protected Defines default field configuration.
generate_id() static public Generates a fallback field ID.
get( $key, $default ) public Reads a field value. Explicit null is treated like a missing value.
set( $key, $value ) public Sets a field value and returns the field instance.
to_array() public Returns all field data as an array.
get_id(), get_type(), get_label(), get_placeholder(), get_admin_label(), get_field_key() public Basic field accessors.
get_title(), get_admin_title() public Returns display names with fallbacks.
is_required(), is_read_only(), allows_empty_submission() public Describe submission behavior. Read-only or special input fields can override these.
shows_label_wrapper(), shows_under_field() public Controls wrapper display behavior.
get_logic_rules() public Returns normalized conditional logic rule groups for rendering.
get_default_value(), get_value() public Returns default or runtime values. Multi-value fields can convert comma-separated defaults to arrays.
is_multiple() protected Return true for fields that submit arrays.
get_field_name(), get_field_id(), get_field_class() public Builds frontend input attributes.
get_wrapper_id(), get_wrapper_class(), get_wrapper_attributes() public Builds frontend wrapper attributes, including conditional logic data.
get_field_attributes() public Builds common frontend field attributes.
get_data( $context ) public Builds render data. Override to add field-specific template values.
get_logic_operators() public Returns allowed conditional logic operators. null means use all operators.
get_string_logic_operators() protected Helper for option/string-comparison fields.
get_logic_value_options(), get_logic_value_source() public Controls value selection in the conditional logic admin UI.
get_admin_field_data() public Returns field data with logic metadata for the admin editor.
get_column_width_class() public Returns responsive column classes for the field wrapper.
validate( $value ) abstract public Validates submitted values. Return true or WP_Error.
sanitize( $value ) abstract public Sanitizes submitted values before storage.
render( $args ) public Renders the field. Template files take priority over render_html().
render_html( $field_data ) protected Fallback inline renderer for custom fields without a template file.
get_template_path() protected Legacy template path helper kept for backward compatibility.
render_field_settings( $tab ) public Adds field-specific admin settings and fires the tab-specific settings action.
sanitize_urlencoded_json( $value ) protected Helper used by text-like fields for URL-encoded JSON payloads.

The base class also implements magic property access and ArrayAccess for backward compatibility. Prefer explicit methods in new code.


Rendering data keys

get_data() returns common template data such as:

  • field, field_id, field_index, field_type, step_index;
  • label, placeholder, under_label, under_field;
  • required, logic_rules, default_value, value;
  • name, id, class, wrapper_id, wrapper_class;
  • form_id, form_settings, texts_settings, width_class.

Field classes can add their own render data. For example, Select adds options, multiple, search, value_label, and has_custom_dropdown.


Template resolution

Rendering checks for a template first:

field-types/field-{type}.php

If a template exists, Elzo Forms loads it through \ElzoForms\Utilities\Template_Loader. If no template exists and the field class implements render_html(), that method is used instead.

Use elzo_forms/templates/locate_template when a plugin needs to provide a custom template path.


Hooks and filters

Hook Purpose
elzo_forms_field_types Register or alter field type labels and class mappings.
elzo_forms_admin_field_tabs Add or alter field settings tabs in the admin builder.
elzo_forms_field_settings_{tab} Add custom controls to a specific field settings tab.
elzo_forms_field_before_save Filter a normalized field array before it is stored.
elzo_forms_step_before_save Filter a normalized step before it is stored.
elzo_forms_form_data_before_save Filter the full form data array before JSON encoding and storage.
elzo_forms_admin_field Filter field data used in admin displays.
elzo_forms_field Filter field data during frontend step rendering.
elzo_forms_is_field_value_empty Customize empty-value detection during submission handling.
elzo_forms_validate_submission Add submission-level validation before field processing continues.
elzo_forms/templates/locate_template Override template resolution.
elzo_forms/templates/template_args Filter template arguments before extraction.
elzo_forms/templates/before_template Run logic before a template is included.
elzo_forms/templates/after_template Run logic after a template is included.
elzo_forms_handle_phone_field Customize telephone field sanitization.

For a broader list of hooks outside the Field API, see PHP Hooks and Filters Reference.


JSON field example

A field definition belongs inside a step in the form JSON.

{
  "id": 101,
  "type": "star_rating",
  "field_key": "customer_rating",
  "label": "How would you rate your experience?",
  "admin_label": "Customer rating",
  "required": true,
  "max_rating": 5,
  "width": {
    "xs": "1/1",
    "md": "1/2"
  },
  "logic": true,
  "rules": [
    [
      {
        "type": "field",
        "operator": "==",
        "settings": {
          "field_id": "100",
          "value": "yes"
        }
      }
    ]
  ]
}

JSON does not register PHP classes. A custom field type must still be registered in PHP before Elzo Forms can create, normalize, render, validate, or sanitize it.


Validation and sanitization

validate() and sanitize() handle submitted frontend values. They are not a replacement for configuration normalization.

Method Input Return
validate( $value ) Submitted value true when valid, or WP_Error when the form should show an error.
sanitize( $value ) Submitted value Cleaned value to store with the submission.
normalize_configuration( $data ) Field definition/configuration Normalized field definition/configuration array.

Best practices

  • Use unique field type keys and prefix custom classes with your plugin namespace.
  • Add every custom configuration key to get_defaults().
  • Normalize custom configuration in normalize_configuration().
  • Validate submitted values with validate() and return actionable WP_Error messages.
  • Sanitize submitted values with sanitize() before storage.
  • Escape all HTML output in templates or render_html().
  • Use $field_data['name'] and $field_data['id'] when rendering inputs.
  • Use is_multiple() for fields that submit arrays.
  • Use allows_empty_submission() for controls that may be absent from $_POST when empty.
  • Do not store secrets in field configuration.