Skip to main content
Version: 4.0 preview

What's new for developers (CBX 4)

Version
4.0 preview
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

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 /cb-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 ConfigboxModelAdminelementsConfigboxModelQuestions, pricing API getElement*()getQuestion*(), assignment/pricing cache keys element_*/*ByElementquestion_*/*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