Skip to main content
Version: 4.0 preview

Rule

Version
4.0 preview
Updated
View markdown

A visibility or pricing rule, authored on a drag-and-drop surface and evaluated by the engine.

Storage kind: column · Column: text

Use it wherever the engine expects a rule — a question's or answer's visibility condition, a shipping or discount rule. It is not a general-purpose structured-data type: the stored JSON is the rule engine's own vocabulary, and only the rule engine reads it. For an arbitrary JSON document use json.

Settings

SettingMeaning
textWhenNoRuleWhat the form shows when no rule is set

Everything else is the shared settings. The editing surface, the condition types and the vocabulary come from the rule engine, not from the property definition.

Validation is not optional here

The engine turns the stored JSON into a PHP boolean expression and eval()s it (ConfigboxRulesHelper::getConditionsCode). The failure mode of a rule that does not hold together is therefore not "behaves oddly":

Malformed howWhat happens at runtime
Conditions not alternating with AND/ORa ParseError
An unknown condition typean exception in every list that renders the rule
A reference to a deleted question, answer or calculationa rule that quietly hides whatever it governs

That last row is the dangerous one — it is silent. check() refuses malformed rules on the way in, and because it is the standard Kenedo validation hook, one gate covers both write paths: the admin form and the MCP storeData, since both go through KenedoModel::validateData().

Every complaint about one rule travels as a single refusal carrying all of them, so the author fixes the whole thing at once rather than one condition per round trip.

Only a changed rule is judged

check() first asks whether this save actually changes the rule, and skips validation if not.

That is load-bearing. KenedoController::store() and the MCP write path both seed the request from the existing record, so an untouched rule is posted back verbatim on every partial update. Without the check, a rule written by an older editor — or against an earlier vocabulary — would block edits to fields that have nothing to do with it: you could not fix a typo in a question's title because its rule had gone stale.

The comparison reads the column directly rather than through getRecord(), whose memoized cache differs between the web process and the CLI. An insert, or any value that cannot be read back, is treated as changed and validated.

Notes

  • It publishes a structured sub-schema to MCP rather than being described as free text, so an assistant authors a rule structurally instead of hand-building the engine's infix token stream. ConfigboxMcpHelper special-cases 'rule' and substitutes ConfigboxMcpRulesHelper::getVocabularySchema() for the field (helpers/mcp.php). That is the exception to the rule that a text column is opaque.
  • copyRule() runs during a record copy, re-pointing references to the copied questions and answers rather than leaving the copy pointing at the original's — the same shape as calculation's copyCalculation(). It honours storeExternally.
  • text is 64 KB. A very large rule needs an explicit wider dataType.

@see json.md — the counter-example, where no structure is imposed @see ../com_configbox_rule_engine.md @see ../com_configbox_mcp_server.md — the sub-schema an assistant authors against @see ../../admin-guide/rules/build-a-rule.md — what the operator sees