Rule Builder UI & Settings Schema Guide

Order Daemon Docs

The Rule Builder UI 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: OrderDaemon\CompletionManager\Core\RuleComponents\Interfaces\ComponentInterface
  • 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 be i18n string IDs resolved with the order-daemon text domain.
  • Use stable, semantic keys that won’t change between versions.

Example

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'),
                'description' => __('rule_component.condition.order_total.operator_description', '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_eq' => __('rule_component.condition.order_total.operator.equal_to', 'order-daemon'),
                    'amount_ne' => __('rule_component.condition.order_total.operator.not_equal_to', '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_eq' => 'amount_eq_value',
                    'amount_ne' => 'amount_ne_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_eq_value' => [
                'type' => 'number',
                'minimum' => 0,
                'default' => 75,
            ],
            'amount_ne_value' => [
                'type' => 'number',
                'minimum' => 0,
                'default' => 75,
            ],
            '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 are 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')
  • Examples:
  • Condition: __('rule_component.condition.order_total.label', 'order-daemon')
  • Action: __('rule_component.action.complete_order.description', 'order-daemon')
  • Trigger: __('rule_component.trigger.payment_complete.title', 'order-daemon')

New in v2.0.0

Enhanced UI Widgets

v2.0.0 introduces several new UI widgets and improvements:

  1. searchable_checkboxes: A powerful widget for multi-select scenarios with search functionality
  2. tiered_checkboxes: Organized checkbox groups with category headers
  3. radio_with_inline_inputs: Radio buttons with associated inline number inputs
  4. Improved validation: Enhanced client-side validation with better error messages

Dynamic Schema Generation

Components can now generate schemas dynamically based on available data:

public function get_settings_schema(): ?array {
    // Get available product categories dynamically
    $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;
            }
        }
    }

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

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, multiselect, radio, checkbox, textarea, currency, number, text, code, searchable_checkboxes, tiered_checkboxes
  • ui:placeholder: Placeholder text (string)
  • ui:help: Short helper text displayed near the input
  • ui:searchable: Boolean; enables search on large selects
  • ui:options: Object for widget‑specific options (e.g., { asyncSource: 'categories', min: 0 })
  • ui:width: Layout width hint (e.g., ‘sm’, ‘md’, ‘lg’, ‘full’)
  • ui:radio_inputs: Maps radio options to inline input fields (new in v2.0.0)

New v2.0.0 Widgets

searchable_checkboxes

Perfect 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:groups' => [
        'Online Payments' => [
            'credit_card' => 'Credit Card',
            'paypal' => 'PayPal',
        ],
        'Offline Payments' => [
            'bank_transfer' => 'Bank Transfer',
        ],
    ],
],

radio_with_inline_inputs

Create radio buttons with associated inline number inputs:

'operator' => [
    'type' => 'string',
    'title' => __('Operator', 'order-daemon'),
    'enum' => [
        'amount_gt' => 'Greater than',
        'amount_lt' => 'Less than',
    ],
    '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,
],

Patterns for common UIs

Select lists (taxonomy, product types)

  • Provide enum/enumNames or ui:options.asyncSource to let the UI fetch options via REST.
  • 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.
  • For order totals, use ui:widget: ‘currency’ when available to render with the store currency.

Boolean flags

  • type: ‘boolean’ renders as a toggle or checkbox; add 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.

Best Practices for v2.0.0

  1. Use dynamic schema generation for fields that depend on available data (product types, categories, etc.)
  2. Leverage new UI widgets like searchable_checkboxes for better user experience with large option sets
  3. Maintain backward compatibility when updating schemas – keep old keys working or provide migrations
  4. Use stable i18n keys following the pattern: __('rule_component.{component_type}.{component_id}.{field_name}', 'order-daemon')
  5. Provide sensible defaults for all fields to ensure new components work out of the box

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.

End‑to‑end example (Condition)

public function get_settings_schema(): ?array {
    // Get available product categories dynamically
    $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;
            }
        }
    }

    // Add fallback if no categories found
    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'),
        'description' => __('rule_component.condition.product_category.description', 'order-daemon'),
        'properties' => [
            'operator' => [
                'type' => 'string',
                'title' => __('rule_component.condition.product_category.operator_label', 'order-daemon'),
                'description' => __('rule_component.condition.product_category.operator_description', 'order-daemon'),
                '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',
                'title' => __('rule_component.condition.product_category.label', 'order-daemon'),
                'description' => __('rule_component.condition.product_category.field_description', 'order-daemon'),
                'enum' => $categories,
                'default' => '0',
                'ui:widget' => 'select',
            ],
        ],
        'required' => ['category'],
    ];
}

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.

Schema Validation Best Practices

  • Test your schemas with various data inputs to ensure proper validation
  • Verify that all enum values are properly translated
  • Test dynamic schema generation with different data states (empty, partial, full)
  • Ensure your component handles missing or invalid settings gracefully

Was this article helpful?

  • Loading...
Table of Contents
  • Loading...