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:
- Select Show or Hide as the action.
- Choose All conditions (AND) or Any condition (OR).
- Add rules by selecting a field, operator, and comparison value.
- 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.