Custom Calc Term Types
- Version
- 4.0 preview
- Updated
How to add your own calculation term type to CBX — a new kind of building block an admin can drop into a formula calculation, which the pricing engine evaluates at configure-time. CBX ships six term types (number, operator, function, calculation, question property, customer group); this guide shows how to add one without touching core.
This is the natural companion to com_configbox_custom_question_types.md: a custom question type
that stores a composite value (a width/height/quantity triple, say) cannot be priced by the shipped
terms, because they reduce a selection with floatval(). A custom term is how that value reaches a
price.
Read com_configbox_customization_overview.md first, and
technical/com_configbox_calculation_engine.md for how calculations are stored and evaluated. All
paths are relative to the component root docroot/components/com_configbox/;
getDirCustomization() is KenedoPlatform::p()->getDirCustomization(). Source references are
point-in-time — verify against the code.
1. How a term type resolves to your class
Unlike question types, term classes are loaded eagerly.
ConfigboxCalcTerm::loadTermClasses() (classes/ConfigboxCalcTerm.php:79) include_onces every
.php file in core classes/calc_term_types/ and in
getDirCustomization()/calc_term_types/. There is no autoloader involvement and no registration
step — dropping the file in is the installation.
The type name is derived from the class name by stripping either accepted prefix
(getTermTypeNames(), classes/ConfigboxCalcTerm.php:131):
$className = str_replace('ConfigboxCalcTerm', '', $className); // built-ins
$className = str_replace('CustomCalcTerm', '', $className); // yours
So both prefixes work, and the convention is:
classes/calc_term_types/ConfigboxCalcTermNumber.php ← shipped → type "Number"
getDirCustomization()/calc_term_types/CustomCalcTermDimensions.php ← yours → type "Dimensions"
That suffix is what gets stored as "type":"Dimensions" in the formula's JSON, so renaming the
class breaks every calculation already using it. Treat the suffix as a published identifier.
2. The contract — three abstract methods
ConfigboxCalcTerm is abstract with exactly three methods to implement
(classes/ConfigboxCalcTerm.php:169-186). Two of them are admin-side; only one runs on the
storefront.
| Method | Runs | Job |
|---|---|---|
getTermsPanelHtml($calculationId, $productId) | Formula editor | The panel an admin uses to insert your term — pickers, inputs, whatever the term needs |
getTermHtml($termData, $forEditing = true) | Formula editor | Render one existing term. $forEditing false means a read-only display |
getTermResult($termData, $selections, $regardingQuestionId = null, $regardingAnswerId = null, $allowNonNumeric = false) | Every price calculation | The value the term contributes |
$termData is your own shape — whatever getTermsPanelHtml() wrote into the formula JSON, handed
back to you. Keep it small and stable; it is persisted.
3. getTermResult() runs a lot
A formula is evaluated on every selection change, for every position in the cart, and again on
cart and checkout pages. getTermResult() therefore sits in the hottest path CBX has.
- No queries per call where a cached read will do. Question data is already cached; reach for
ConfigboxQuestion::getQuestion()rather than SQL. - Return a number. Unless
$allowNonNumericis true, the engine is composing an arithmetic expression; returning a string ornullpropagates into the whole formula. $selectionsis the current configuration. Read from it rather than re-fetching state.
4. The editor methods have a failure mode worth knowing
getTermHtml() renders terms an admin saved earlier — including terms that point at a question
which has since been deleted. If your implementation resolves a question id without checking,
the lookup throws, and the whole formula editor fails to render: the admin cannot open the
calculation, and cannot delete the stale term either, because they cannot reach it.
Guard it:
if (!ConfigboxQuestion::questionExists($questionId)) {
return '<span class="cb-term cb-term-broken">' . KText::_('Deleted question') . '</span>';
}
ConfigboxQuestion::questionExists() (classes/ConfigboxQuestion.php:155) exists for exactly
this. A placeholder keeps the editor usable and lets the admin remove the term.
5. Worked shape — pricing a composite question
For a dimensions question storing {"w":1200,"h":800,"qty":2}, a term that exposes one
sub-value:
defined('CB_VALID_ENTRY') or die();
class CustomCalcTermDimensions extends ConfigboxCalcTerm {
/** The sub-values this term can pull out of a dimensions selection. */
protected function getSubValues() {
return ['w' => KText::_('Width'), 'h' => KText::_('Height'), 'qty' => KText::_('Quantity')];
}
public function getTermResult($termData, $selections, $regardingQuestionId = null, $regardingAnswerId = null, $allowNonNumeric = false) {
if (empty($termData['questionId']) || empty($selections[$termData['questionId']])) {
return 0;
}
$decoded = json_decode($selections[$termData['questionId']], true);
$key = $termData['subValue'] ?? 'w';
return isset($decoded[$key]) ? (float) $decoded[$key] : 0;
}
// getTermsPanelHtml() — a question picker plus a sub-value dropdown
// getTermHtml() — "Width of «Frame size»", guarded per §4
}
The point is the decode: the selection is JSON, so the shipped question-property term would read
0. That decode is the entire reason the type exists.
6. Deployment checklist
getDirCustomization()/calc_term_types/CustomCalcTerm<Type>.php— implement all three methods.- Clear the CBX cache; on some platforms CBX keeps its own file cache the host's cache-clear does not touch.
- Open a formula calculation in the admin — your panel should be offered alongside the shipped terms. If it is absent, the file is not in the folder the platform resolves (§1).
- Insert the term, save, and check a price on the storefront —
getTermsPanelHtml()working does not provegetTermResult()does.
7. Gotchas
- The class-name suffix is a stored identifier. Renaming the class orphans every formula using it (§1).
- Both prefixes are stripped, so
ConfigboxCalcTermFooandCustomCalcTermFooboth yield typeFoo— do not ship both. - Guard question lookups in
getTermHtml()or a deleted question takes the formula editor down with it (§4). - Loading is eager and unconditional: a parse error in your file breaks calculation loading everywhere, not just where the term is used.
getTermResult()is hot — see §3.
See also
com_configbox_custom_question_types.md— the composite values these terms usually exist to readcom_configbox_customization_overview.md— the layer, and where it lives per platformtechnical/com_configbox_calculation_engine.md— storage, the three calculation types, evaluationcom_configbox_custom_rule_conditions.md— the same pattern for the rule engine