DOCS

v1.3.28
 // Pro v1.2.20

 · Latest

Docs/Extending Order Daemon/Settings Schemas

Settings Schemas

This guide explains how to define component settings schemas that the Rule Builder UI can render, how to add UI metadata, and how to structure component settings.

What is a settings schema?

Every Trigger, Condition, and Action can declare a settings schema via:

  • Interface: OrderDaemonCompletionManagerCoreRuleComponentsInterfacesComponentInterface
  • Method: get_settings_schema(): ?array

The schema is a PHP array that uses a JSON-schema-like structure. The Rule Builder consumes this schema to render fields, validate input, and persist configuration.

General rules:

  • Return null or [] if your component has no settings.
  • Keep IDs stable. Saved rule JSON references your schema keys.
  • Labels and descriptions must use i18n string IDs resolved with the order-daemon text domain.
  • Use stable, semantic keys that won’t change between versions.
public function get_settings_schema(): ?array {
    return [
        'type'        => 'object',
        'title'       => __('rule_component.condition.order_total.label', 'order-daemon'),
        'description' => __('rule_component.condition.order_total.description', 'order-daemon'),
        'properties'  => [
            'operator' => [
                'type'            => 'string',
                'title'           => __('rule_component.condition.order_total.operator_label', 'order-daemon'),
                'enum'            => [
                    'amount_gt'  => __('rule_component.condition.order_total.operator.greater_than', 'order-daemon'),
                    'amount_lt'  => __('rule_component.condition.order_total.operator.less_than', 'order-daemon'),
                    'amount_gte' => __('rule_component.condition.order_total.operator.greater_than_equal', 'order-daemon'),
                    'amount_lte' => __('rule_component.condition.order_total.operator.less_than_equal', 'order-daemon'),
                ],
                'ui:radio_inputs' => [
                    'amount_gt'  => 'amount_gt_value',
                    'amount_lt'  => 'amount_lt_value',
                    'amount_gte' => 'amount_gte_value',
                    'amount_lte' => 'amount_lte_value',
                ],
                'default' => 'amount_gt',
            ],
            'amount_gt_value'  => ['type' => 'number', 'minimum' => 0, 'default' => 100],
            'amount_lt_value'  => ['type' => 'number', 'minimum' => 0, 'default' => 50],
            'amount_gte_value' => ['type' => 'number', 'minimum' => 0, 'default' => 100],
            'amount_lte_value' => ['type' => 'number', 'minimum' => 0, 'default' => 50],
        ],
        'required' => ['operator'],
    ];
}

Supported types and common keywords

Root container:

  • type: object – most component schemas use an object root with properties
  • properties: array – keyed by field ID
  • required: string[] – list of required property keys

Field types:

  • string – with optional enum, format, pattern
  • number/integer – with optional minimum/maximum, multipleOf
  • boolean – renders as a toggle/checkbox
  • array – with items (type or schema), uniqueItems, minItems, maxItems
  • object – nested objects supported for grouped settings

Common keywords:

  • title – i18n label for the field
  • description – i18n helper text
  • default – default value
  • enum – fixed list of values (strings or numbers)
  • enumNames – i18n labels for enum values (optional)
  • minimum/maximum – numeric bounds
  • pattern – regex for strings
  • examples – example values for docs/tooling

i18n guidance: Always wrap labels/descriptions in translation functions with text domain order-daemon. Use stable string keys following the pattern: __('rule_component.{component_type}.{component_id}.{field_name}', 'order-daemon').

UI metadata (ui:* hints)

The Rule Builder supports ui:* hints to influence rendering. These are advisory; the editor may fall back to a reasonable default if a widget is unknown.

Common UI hints

  • ui:widget – choose a widget: select, textarea, number, text, searchable_checkboxes, tiered_checkboxes, button_radio_group, date_picker, time_picker
  • ui:placeholder – placeholder text (string)
  • ui:searchable – boolean; enables search on large selects
  • ui:radio_inputs – maps radio options to inline input fields

searchable_checkboxes

For large multi-select scenarios like product types:

'product_types' => [
    'type'         => 'array',
    'title'        => __('rule_component.condition.product_type.field_label', 'order-daemon'),
    'items'        => ['type' => 'string', 'enum' => $product_types],
    'ui:widget'    => 'searchable_checkboxes',
    'ui:searchable' => true,
    'ui:placeholder' => __('Search product types...', 'order-daemon'),
    'default'      => ['virtual', 'downloadable'],
],

tiered_checkboxes

Organize options into logical groups:

'payment_methods' => [
    'type'  => 'array',
    'title' => __('Payment Methods', 'order-daemon'),
    'items' => [
        'type' => 'string',
        'enum' => [
            'credit_card'   => 'Credit Card',
            'paypal'        => 'PayPal',
            'bank_transfer' => 'Bank Transfer',
        ],
    ],
    'ui:widget' => 'tiered_checkboxes',
    'ui:tiers'  => [
        'online'  => ['types' => ['credit_card', 'paypal']],
        'offline' => ['types' => ['bank_transfer']],
    ],
],

button_radio_group

Renders an enum field as a compact button-group (pill buttons). Ideal for short option lists like operators or match modes. Used by ProductCategoryCondition, ProductTypeCondition, and others.

'operator' => [
    'type'      => 'string',
    'title'     => __('rule_component.condition.product_category.operator_label', 'my-plugin'),
    'enum'      => [
        'in'     => __('Contains any of', 'my-plugin'),
        'not_in' => __('Does not contain', 'my-plugin'),
        'all_in' => __('Contains all of', 'my-plugin'),
    ],
    'default'   => 'in',
    'ui:widget' => 'button_radio_group',
],

radio_with_inline_inputs

Renders radio buttons where each option has an associated inline number input. The ui:radio_inputs map points each enum value to a sibling property key that holds its number. Used by OrderTotalAmountCondition.

'operator' => [
    'type'            => 'string',
    'title'           => __('Operator', 'my-plugin'),
    'enum'            => [
        'amount_gt' => __('Greater than', 'my-plugin'),
        'amount_lt' => __('Less than', 'my-plugin'),
    ],
    'ui:radio_inputs' => [
        'amount_gt' => 'amount_gt_value',
        'amount_lt' => 'amount_lt_value',
    ],
    'default' => 'amount_gt',
],
'amount_gt_value' => ['type' => 'number', 'minimum' => 0, 'default' => 100],
'amount_lt_value' => ['type' => 'number', 'minimum' => 0, 'default' => 50],

date_picker / time_picker Pro

Available in Pro components. Render a native date or time input. Used by TimingCondition.

'date' => [
    'type'      => 'string',
    'title'     => __('Date', 'my-plugin'),
    'ui:widget' => 'date_picker',
],
'time' => [
    'type'      => 'string',
    'title'     => __('Time', 'my-plugin'),
    'ui:widget' => 'time_picker',
],

Patterns for common UIs

Select lists (taxonomy, product types):

  • Provide enum/enumNames with values populated dynamically in get_settings_schema().
  • Use ui:searchable for long lists. For multi-select, set type: 'array' with items: { type: 'string' }.

Numeric inputs (currencies, totals):

  • Prefer type: 'number'; add minimum/maximum and sensible defaults.

Boolean flags:

  • type: 'boolean' renders as a toggle or checkbox; add a description for clarity.

Nested groups:

  • Use type: 'object' for grouped settings; provide a title to render a sub-section.

Conditional fields:

  • Keep schemas static and handle conditional show/hide in UI hints where possible.
  • If you must switch fields based on another field’s value, prefer a single field with enum + ui:widget and handle branching inside your component at runtime.

Dynamic schema example

public function get_settings_schema(): ?array {
    $categories = [];
    if (function_exists('get_terms')) {
        $terms = get_terms(['taxonomy' => 'product_cat', 'hide_empty' => false]);
        if (!is_wp_error($terms)) {
            foreach ($terms as $term) {
                $categories[$term->term_id] = $term->name;
            }
        }
    }
    if (empty($categories)) {
        $categories = ['0' => __('rule_component.condition.product_category.no_categories_found', 'order-daemon')];
    }

    return [
        'type'       => 'object',
        'title'      => __('rule_component.condition.product_category.label', 'order-daemon'),
        'properties' => [
            'operator' => [
                'type'    => 'string',
                'enum'    => [
                    'in'     => __('rule_component.condition.product_category.operator.in', 'order-daemon'),
                    'not_in' => __('rule_component.condition.product_category.operator.not_in', 'order-daemon'),
                    'all_in' => __('rule_component.condition.product_category.operator.all_in', 'order-daemon'),
                ],
                'default' => 'in',
            ],
            'category' => [
                'type'      => 'string',
                'enum'      => $categories,
                'default'   => '0',
                'ui:widget' => 'select',
            ],
        ],
        'required' => ['category'],
    ];
}

Validation and defaults

  • The editor performs light validation using your schema (required, enum, min/max). Perform server-side validation in your REST/controller or component logic.
  • Always set defaults so new fields don’t break existing saved rules. Never remove or rename keys without a migration path.
  • For arrays with enum values, ensure payload sanitization on save.
  • Use the sanitize_by_schema method in the Evaluator class for consistent validation.

Schema migrations

Settings are persisted as JSON in saved rules. If you change your schema after rules have been saved, follow these rules to avoid breaking existing rules:

  • Adding a field – always provide a default. The Evaluator calls sanitize_by_schema() before passing settings to evaluate(), which fills missing keys with their defaults.
  • Renaming a key – keep the old key working in evaluate() by aliasing it: $value = $settings['new_key'] ?? $settings['old_key'] ?? $default;. Remove the alias once you’re confident no saved rules reference the old key.
  • Removing an enum value – never remove a value that may exist in saved rules. Add a fallback branch in evaluate() that handles the removed value gracefully.
  • Removing a field entirely – safe to do in the schema (the UI will stop showing it), but keep handling the key in evaluate() for backwards compatibility.

The sanitize_by_schema() method on Evaluator strips unknown keys and fills missing keys with defaults. Rely on it rather than writing your own sanitization.

Testing and troubleshooting

  • Verify i18n: ensure the order-daemon text domain is loaded and JSON script translations are registered for the Rule Builder assets.
  • Backwards compatibility: when changing schemas, keep old keys working or add a migration. Avoid removing enum values that may exist in saved rules.
  • Performance: large async selects should page results server-side; avoid heavy synchronous PHP in options generation.