Skip to main content
Version: 3.x

What's new for developers (CBX 4)

Version
3.x
Updated
View markdown

New and changed capabilities that matter when you build on, extend, or operate CBX 4 as a developer. For how to fix customization code broken by these, see the breaking-changes log and the playbooks; this page is the "what you now have" companion.

This page is about the jump to 4.0. For what has been built since — the HTTP API, the MCP server, the CLI suite, generated record classes and schemas, prepared statements — see Latest features, and the HTTP API, the MCP server and platform & extensibility in particular. That set is cut by surface rather than by audience, so it is a different read, not a continuation of this one.

Simpler answer model​

  • One #__configbox_answers table replaces the old options + xref_element_option pair. Every answer belongs to exactly one question (clean 1:n) and is a self-contained row: its price/sku/weight/ images/custom fields and its title/description strings all hang off the answer id. No more "global option" reuse, no more merge-at-cache-time, no more two-ids-per-answer.
  • Why you'll like it: answer queries are single-table; the answer id is the one identity to reason about; the admin has a single Answers editor instead of a global-options list + an assignment editor.
  • Compat shims mean cleanly-written customizations keep working (legacy getModel() aliases, legacy model_property_customization loading).

Read more: the answer collapse, in depth — the old and new shapes, what the migration does to your data, and every shim · looking up assignments · building a product, from the top

Rules and formulas read fields the question type declares​

  • A question type declares what a rule or a formula can read off it — getRuleFields(), getCalcFields() and getFieldValue($key, $selection, $answerId, $selections) on ConfigboxQuestion<Type>. The one generic question condition and term render the chips under the question, validate on save, evaluate, remap on copy and transfer, and describe the fields to the AI authoring tools. A composite type (dimensions: w, h, area) declares its members as fields; a per-answer quantity is a field declared once per answer. No more condition class, term class and editor sub-view per question type — classes you still ship keep working under "Other conditions".
  • One stored vocabulary: {"type":"QuestionProperty","questionId":42,"field":"selected","operator":"==","value":"57"}. selectedAnswer.id, selection, fieldPath and the pre-3.6 data wrapper are gone from the data (migrations 3.8.26 and 3.8.29) and tolerated on read.
  • Comparison per field kind (ConfigboxQuestionField::matches()): numbers as numbers, dates by day, choices and texts as strings; an unanswered question is null, never 0, so an ordering on an empty number field is false and an empty condition value means "not answered".
  • The question type is fixed once saved, except for a change to a type the stored type declares compatible (getCompatibleTypes()): the answer-taking types among each other, the text types among each other. The check is on the model, so every writer is refused alike.
  • Price helpers take the set they price: ConfigboxPrices::getQuestionPrice($id, array $selections, …) and the answer, recurring and weight helpers alike. A rule on a preview or a term in a formula never prices the live session by accident again; the deprecated getElement* aliases keep the live default.
  • The installer removes what the version no longer ships (helpers/retired_files.txt), so a directory scan cannot resurrect a retired condition type after an upgrade.

Read more: the breaking-changes log, entries 2026-09-06 and 2026-09-07 · custom question types, §2.6 "Rules and formulas — the type declares its fields" and §2.9 "Two more things a type may declare" (getCompatibleTypes()) · the rule engine §4.3 · the calculation engine

Reactive configurator client state (a Redux-style store)​

  • The configurator page's client state now lives in a small Redux-style store — a new AMD module configbox/store (assets/javascript/store.js) — instead of a write-once jQuery blob on #configurator-data. It's a single immutable state tree with dispatch(action) / getState() / subscribe(listener) / selectors, normalised entities.questions (by-id) + a per-question selections map, and one MERGE_SERVER_RESPONSE action that folds each configurator server response in atomically. The state is maintained through interactions (a selection round-trip updates missingProductSelections, pricing, the recorded selection, …) — the old blob drifted out of sync.
  • Backward-compatible. The classic configurator.getConfiguratorData() / setConfiguratorDataItem() / replaceConfiguratorData() / getQuestionPropValue() / questionHasProperty() accessors are now thin shims over the store, and the store mirrors a flat snapshot back into #configurator-data's jQuery .data('json') after every change. New code can use configurator.getStore() or cbrequire(['configbox/store'], …) directly.
  • Gotcha: the DOM #configurator-data data-json attribute is only the frozen initial seed — read the store (or the accessor) for current state. And jQuery is AMD-scoped (cbj), not a global.

Read more: the configurator client store — the state tree, every action and selector, the legacy bridge, and how customization JS should read and watch it · the configurator loop, server and client · the dated store entry in the breaking-changes log

A real CLI command suite (Joomla 5 console)​

Run via php cli/joomla.php configbox:<cmd>, grouped under the configbox namespace:

  • configbox:cache:clear — clear all CB caches.
  • configbox:migrate — apply outstanding migration scripts (--clear-failed-flag to recover a frozen update; --status to report what is pending without applying anything; -v for the full failure diagnostics — exception chain, stack trace, migration-log tail).
  • configbox:migrate:unblock — lift the freeze a failed migration puts on the update system. Explains the block first (which script, which error, whether a run is in progress), then clears it; --dry-run to look without touching, --skip-version=<v> to mark an unfixable script as applied without running it.
  • configbox:run-task <controller> [task] [key=value…] [--user …] [--output-mode …] — run any CB controller task from the CLI exactly as the /cbx-api/ endpoint would (inject GET/POST-style params, impersonate a user so CB behaves as if they're logged in, capture the chrome-free output).
  • configbox:config:get|set|list — read/write admin settings through ConfigboxModelConfig, so a set runs the same validation a UI save runs; handles translatables per --language; list flags which settings are editable.
  • configbox:sysvar:get|set|list — the low-level #__configbox_system_vars bookkeeping store.

Reusable core: all the logic is in a platform-agnostic ConfigboxCliHelper (Kenedo, no Joomla), so the same commands can be re-exposed on WordPress (WP-CLI) and Magento with thin wrappers. Registration is a console-group plugin (Joomla 5 has no component console-command tag).

Read more: the CLI command suite — every command with its arguments, options and exit codes, the core/wrapper split, and the WordPress and Magento invocations · what the suite has gained since · adding your own commands in the customization layer

Admin light / dark / auto theming​

  • The backend supports light/dark/auto, anchored on the standard data-bs-theme attribute (Bootstrap 5.3 colour-mode). On Joomla it inherits the Atum template's choice automatically; other platforms resolve it via a new KenedoPlatform::getAdminColorScheme() method (prefers-color-scheme fallback for auto).
  • Admin colours are now CSS custom properties (--cb-*) in assets/css/admin-theme.css — a real token layer with light/dark/auto blocks and vendor (Tom Select / jQuery UI) overrides. Custom admin CSS should reference these tokens so it flips with the scheme.

Read more: admin theming — the --cb-* token layer — how each platform resolves the scheme, the full token set, the vendor chrome, and the rules custom admin CSS has to follow · host-adaptation stylesheets · the dark-mode entry in the breaking-changes log

Better migration ergonomics​

  • The whole update-script workflow is written down: the idempotency helpers (tableExists/tableFieldExists/keyExists/getFkConstraintName), the failed_update_detected freeze, the dedicated configbox_upgrade_errors.log, and the CLI debug loop.

  • Migrations can now be driven explicitly from the CLI (configbox:migrate) instead of only implicitly on init — so a deploy step surfaces failures in your deploy log, not to the first visitor.

  • Pending migrations are now visible. CBX tracks its schema version in #__configbox_system_vars, not in the host's extension registry — which is why Joomla's extension version and Magento's setup:db:status both report "up to date" while CBX migrations are outstanding, and the only symptom is a 500 on a product page. configbox:migrate --status reports the installed version, the newest shipped one, and every pending core/customization script, applies nothing, and exits non-zero when work is due — so a deploy or CI step can gate on it:

    php cli/joomla.php configbox:migrate --status

    The equivalent raw check is SELECT value FROM #__configbox_system_vars WHERE \key` = 'latest_update_version', compared against the newest file in helpers/updates/`.

  • Ordering to be aware of: applyUpdates() runs all core scripts, then all customization scripts. Harmless on an existing site; on a fresh install it means core migrates to current (renaming #__configbox_elements) before your historical customization scripts run against the new schema. Write customization update scripts defensively, or collapse them into a current-vocabulary baseline.

Read more: database migrations — writing an idempotent update script, both tracks, the freeze and how to lift it, and the debug loop · the CLI reference · what deploy tooling has gained since

Customization compatibility as a first-class concern​

  • The core ships auto-conversion shims for the answer collapse (model aliases, legacy customization-file loading with key remaps, graceful log-and-skip instead of white-screening).
  • This migration-to-cb4/ docs area is the standing home for every breaking change: a running log, a per-kind playbook, a deep dive, and per-audience orientation. New breaking changes get logged here as a rule (see the README's rules section).

Read more: the customization layer, in full — every extension point, where each resolves, and which mechanism to reach for · model-property customizations after the collapse · what the layer can now extend

Housekeeping you should know about​

  • Public pricing API renamed ConfigboxPrices::getXref*() → getAnswer*(); cache keys *ByXref → *ByAnswer and xref_to_* → answer_to_question (the answer-collapse first produced answer_to_element, then the element→question rename renamed it again; the old forms remain as aliases).

Read more: the old → new identifier cheat-sheet — every renamed identifier with its status (shim, manual, manual+data) and a grep pack to find the hits · the breaking-changes log

The "element" entity is now "question" (code + DB + engine)​

  • The configurator entity that the UI always called a question was still element in the code, DB and admin MVC. CBX 4 finishes the transition: table #__configbox_elements → #__configbox_questions, every *element_id*/feature column → question_*, model ConfigboxModelAdminelements → ConfigboxModelQuestions, pricing API getElement*() → getQuestion*(), assignment/pricing cache keys element_*/*ByElement → question_*/*ByQuestion, the rule/calc classes Configbox{Condition,CalcTerm}ElementAttribute → …QuestionProperty, and the calc DSL tokens (ElementAttribute( → QuestionProperty(, etc.). The entity id is unchanged.
  • The DB half ships as helpers/updates/3.5.2.php (applied by configbox:migrate); the stored-rules transform is model-driven (it discovers and converts custom rule columns too). Backward-compat shims cover the model class, pricing methods, cache keys and the customization-file name; the rule/calc type strings and DSL tokens are a hard rename (stored data migrated, no alias).

Read more: the element → question rename, before → after — the full identifier map and the per-file decision procedure · the old → new cheat-sheet · the rule engine and the calculation engine, whose vocabularies changed with it