Skip to main content

Conditional Logic

Core Forms supports conditional field visibility and action execution using both a legacy attribute-based format and a structured JSON format. Conditions are evaluated on the frontend in real time and on the server side for security.

Frontend: Data Attributes

Show/Hide Attributes

Add data-show-if or data-hide-if attributes to any form element to control its visibility based on another field's value:

<!-- Show the company field only when "Business" is selected -->
<div data-show-if="account_type=Business">
  <label for="company">Company Name</label>
  <input type="text" name="company" id="company">
</div>

<!-- Hide the discount code field when "Free" plan is selected -->
<div data-hide-if="plan=Free">
  <label for="discount_code">Discount Code</label>
  <input type="text" name="discount_code" id="discount_code">
</div>

Legacy Format Syntax

The legacy format supports several matching patterns:

<!-- Exact match -->
<div data-show-if="role=Manager">

<!-- Multiple values (OR) separated by pipe -->
<div data-show-if="role:Manager|Director|VP">

<!-- Any non-empty value (wildcard) -->
<div data-show-if="company=*">

<!-- Exact match with equals sign -->
<div data-hide-if="newsletter=no">

Format rules:

Pattern Meaning
field=value Show/hide when field equals value
field:val1\|val2\|val3 Show/hide when field matches any listed value
field=* Show/hide when field has any non-empty value

Hidden fields are excluded from submission data and validation. When a field is hidden by conditional logic, its required attribute is temporarily removed.

Structured JSON Format

For complex conditions, use the structured format in your form schema. This supports 14 operators, grouped conditions, and nested logic.

Basic Condition

{
  "conditions": {
    "action": "show",
    "rules": [
      { "field": "account_type", "operator": "=", "value": "Business" }
    ]
  }
}

Supported Operators

Operator Description Example Value
= Equals "Business"
!= Not equals "Free"
> Greater than 100
< Less than 50
>= Greater than or equal 18
<= Less than or equal 65
contains String contains substring "gmail"
not_contains String does not contain "test"
starts_with String starts with "Dr."
ends_with String ends with "@company.com"
is_empty Field is empty (no value needed)
is_not_empty Field is not empty (no value needed)
in Value is in list ["red", "blue", "green"]
not_in Value is not in list ["spam", "test"]

Grouped Conditions: AND / OR Logic

Use the match property to control how multiple rules are combined:

{
  "conditions": {
    "action": "show",
    "match": "all",
    "rules": [
      { "field": "age", "operator": ">=", "value": 18 },
      { "field": "country", "operator": "=", "value": "US" }
    ]
  }
}
  • "match": "all" -- all rules must be true (AND logic). This is the default.
  • "match": "any" -- at least one rule must be true (OR logic).

Nested Groups

For complex logic like "(A AND B) OR (C AND D)", nest condition groups:

{
  "conditions": {
    "action": "show",
    "match": "any",
    "rules": [
      {
        "match": "all",
        "rules": [
          { "field": "role", "operator": "=", "value": "Manager" },
          { "field": "department", "operator": "=", "value": "Engineering" }
        ]
      },
      {
        "match": "all",
        "rules": [
          { "field": "role", "operator": "=", "value": "Director" },
          { "field": "department", "operator": "in", "value": ["Engineering", "Product"] }
        ]
      }
    ]
  }
}

This shows the field when the user is either an Engineering Manager or a Director in Engineering or Product.

Server-Side Evaluation

The ConditionEvaluator class handles server-side condition checks. This is critical for security -- frontend conditions can be bypassed, so the server re-evaluates all conditions during form processing.

ConditionEvaluator Class

use Core_Forms\ConditionEvaluator;

// Evaluate a single condition
$evaluator = new ConditionEvaluator();

$result = $evaluator->evaluate([
    'field'    => 'account_type',
    'operator' => '=',
    'value'    => 'Business',
], $submission_data);
// Returns: true or false

// Evaluate a condition group
$result = $evaluator->evaluate_group([
    'match' => 'all',
    'rules' => [
        ['field' => 'age', 'operator' => '>=', 'value' => 18],
        ['field' => 'country', 'operator' => '=', 'value' => 'US'],
    ],
], $submission_data);

Used in Action Conditions

The same ConditionEvaluator powers conditional actions (emails, webhooks, etc.). You can configure an action to only fire when certain conditions are met:

{
  "actions": [
    {
      "type": "email",
      "to": "sales@example.com",
      "subject": "New Enterprise Lead",
      "conditions": {
        "match": "all",
        "rules": [
          { "field": "plan", "operator": "=", "value": "Enterprise" },
          { "field": "team_size", "operator": ">", "value": 50 }
        ]
      }
    },
    {
      "type": "email",
      "to": "support@example.com",
      "subject": "New Free Signup",
      "conditions": {
        "match": "all",
        "rules": [
          { "field": "plan", "operator": "=", "value": "Free" }
        ]
      }
    }
  ]
}

In this example, the sales team only receives email for Enterprise leads with 50+ team members, while support gets notified for Free signups.

Builder UI: Conditions Section

When using the form builder, each field's inspector panel includes a Conditions section:

  1. Select Show or Hide as the action.
  2. Choose All conditions (AND) or Any condition (OR).
  3. Add rules by selecting a field, operator, and comparison value.
  4. Add nested groups for complex logic via the "Add Group" button.

The builder generates the structured JSON format automatically.

Examples

Dependent Dropdowns

Show a city dropdown based on the selected state:

<select name="state" id="state">
  <option value="">Select State</option>
  <option value="CA">California</option>
  <option value="NY">New York</option>
</select>

<div data-show-if="state=CA">
  <select name="city" id="city_ca">
    <option value="LA">Los Angeles</option>
    <option value="SF">San Francisco</option>
  </select>
</div>

<div data-show-if="state=NY">
  <select name="city" id="city_ny">
    <option value="NYC">New York City</option>
    <option value="BUF">Buffalo</option>
  </select>
</div>

Conditional Required Fields

When a field becomes visible via conditions, you often want it to be required only when shown:

<div data-show-if="has_referral=yes">
  <label for="referral_code">Referral Code <span class="required">*</span></label>
  <input type="text" name="referral_code" id="referral_code" required>
</div>

When has_referral is not "yes", the wrapper is hidden, required is removed from the input, and the field is excluded from validation.

Structured Format: Survey Branching

{
  "fields": [
    {
      "name": "satisfaction",
      "type": "radio",
      "label": "How satisfied are you?",
      "options": ["Very Satisfied", "Satisfied", "Neutral", "Dissatisfied", "Very Dissatisfied"]
    },
    {
      "name": "what_went_well",
      "type": "textarea",
      "label": "What did you enjoy most?",
      "conditions": {
        "action": "show",
        "rules": [
          { "field": "satisfaction", "operator": "in", "value": ["Very Satisfied", "Satisfied"] }
        ]
      }
    },
    {
      "name": "what_to_improve",
      "type": "textarea",
      "label": "What could we improve?",
      "conditions": {
        "action": "show",
        "rules": [
          { "field": "satisfaction", "operator": "in", "value": ["Dissatisfied", "Very Dissatisfied"] }
        ]
      }
    }
  ]
}

This shows different follow-up questions depending on the user's satisfaction rating.