Overriding Views & Templates
- Version
- 3.x
- Updated
The most common customization task: change what a screen looks like without editing core files. This guide covers the two layers that produce a screen — the template (the markup, the usual target) and the view class (the data prep behind it, rarely overridden) — how each resolves, the exact precedence order (with one important asymmetry between them), what variables a template has, and a worked example.
Read com_configbox_customization_overview.md first for the layer map and the resolution rule this guide
specialises. All paths are relative to the component root docroot/components/com_configbox/. Source
references are point-in-time (component 3.4.1) — verify against the code.
1. View vs. template — which one do you want?
A CBX screen is rendered by two cooperating pieces:
- The view class —
views/<view>/view.html.php, aKenedoViewsubclass. It prepares the data: loads the record(s), assigns$this->record/$this->records/$this->properties, builds links, computes the page title. It decides what data the screen has. - The template —
views/<view>/tmpl/<layout>.php, a plain PHP file. It renders the markup using the data the view prepared. It decides how the data looks.
Rule of thumb: 90% of the time you want a template override. Changing markup, layout, classes, the order of fields on a page, adding a wrapper or a bit of static content — all template work. Override the view class only when you need to change the data a screen has (load an extra record, change a query, alter the page title logic). Template overrides are smaller and drift less across updates.
2. Overriding a template (the common case)
2.1 The resolution chain
Template selection happens in KenedoView::renderView() (external/kenedo/classes/KenedoView.php:459-506).
It builds three candidate paths and uses the first that exists:
$viewName = strtolower( basename( dirname($this->getViewPath()) ) ); // e.g. "product"
$templatePaths['templateOverride'] = KenedoPlatform::p()->getTemplateOverridePath($this->component, $viewName, $template);
// → JPATH_SITE/templates/<active-template>/html/<component>/<view>/<layout>.php
$templatePaths['customTemplate'] = KenedoPlatform::p()->getDirCustomization().'/templates/'.$viewName.'/'.$template.'.php';
// → data/customization/templates/<view>/<layout>.php
$templatePaths['defaultTemplate'] = dirname($this->getViewPath()).'/tmpl/'.$template.'.php';
// → views/<view>/tmpl/<layout>.php (the shipped core template)
foreach ($templatePaths as $templatePath) {
if (is_file($templatePath)) { include($templatePath); break; } // first match wins
}
So the precedence, highest first, is:
| Slot | Path | When to use |
|---|---|---|
| 1. Joomla template override | templates/<active-template>/html/com_configbox/<view>/<layout>.php (general.php:1040) | Markup that is specific to one Joomla site template (the Joomla-native slot). |
| 2. Customization template | data/customization/templates/<view>/<layout>.php | The platform-neutral, upgrade-safe slot — use this by default. |
| 3. Core template | views/<view>/tmpl/<layout>.php | The shipped default. Don't edit it; copy it up to slot 2. |
Use slot 2 (data/customization/templates/) for customization work. Slot 1 is the Joomla-specific
escape hatch (handy if the same install runs multiple Joomla templates that need different markup); slot 3
is core and off-limits.
2.2 Identifying the view and layout names
You need two names to place a template: the view name (the directory) and the layout name (the file).
- View name = the core view directory under
views/. E.g. the product page isviews/product/, so the view name isproduct; the radio-button question widget isviews/question_radiobuttons/. - Layout name = the value of the
layoutrequest keyword, defaulting todefault(renderView(),:466-472). Most views ship adefault.php; some ship extra layouts (views/product/tmpl/hasdefault.php,notfound.php,metadata.php). The layout name is sanitised against path traversal before use (:474-475), so it is always a bare filename.
Therefore, to override the product page's default layout you create:
data/customization/templates/product/default.php
Copy, don't reference. Start by copying the core template
views/<view>/tmpl/<layout>.phptodata/customization/templates/<view>/<layout>.php, then edit the copy. There is no "parent template" call — your file fully replaces the core one for that view+layout. Copy only the one layout file you need to change; other layouts of the same view keep using core.
2.3 What a template can use ($this is the view)
The template is include()d inside the view object's scope, so $this is the KenedoView subclass
and every property the view prepared is available. The view runs prepareTemplateVars()
(:282-293) before rendering, which assigns different variables for list vs form/detail screens:
Detail/form views (prepareTemplateVarsForm(), :375-402) expose:
| In the template | Is | From |
|---|---|---|
$this->record | the loaded record (or initData() defaults for a new one) | :380-385 |
$this->properties | the model's KenedoProperty objects (drive the form) | :390 |
$this->pageTitle | computed page title | :392-398 |
$this->formAction | the form's submit URL | :387 |
$this->pageTasks | toolbar tasks (save/delete/…) | :400 |
List views (prepareTemplateVarsList(), :295-373) expose $this->records, $this->properties
(listing columns), $this->pagination, $this->filters/$this->filterInputs, $this->orderingInfo,
$this->listingData, $this->pageTasks.
Custom views (your own frontend screens) typically assign their own variables in the view class via
$this->assign($key, $value) (:516-518) or by setting $this->foo directly, then read $this->foo in
the template.
Escaping is your job. Templates output directly. Use
hsc()on every dynamic value (hsc($this->record->title)); the codebase has no auto-escaping. For property-driven forms, render each property through its own output methods rather than reading raw columns — see the property guide.
2.4 A worked template override
Suppose you want the product page to show a short "configurator help" banner above the configurator. You do not need new data, so a template override is enough.
- Find the core template:
views/product/tmpl/default.php. - Copy it to the customization slot:
data/customization/templates/product/default.php. - Edit the copy — add your banner near the top, keeping the rest intact:
<?php
defined('CB_VALID_ENTRY') or die();
/** @var $this ConfigboxViewProduct */ // $this is the view; see the core template's existing header
?>
<div class="cb-configurator-help">
<?php echo hsc(KText::_('CB_CUSTOM_CONFIGURATOR_HELP')); ?>
</div>
<?php /* …the rest of the original core template, unchanged… */ ?>
That's it: no class, no controller, no core edit. The next component update overwrites
views/product/tmpl/default.php but never your data/customization/templates/product/default.php, so
your banner survives.
Because your copy fully replaces the core template, re-check it after a component update that changed the original
default.php— your copy won't have picked up the upstream change. Keep overrides minimal (ideally a small diff from core) to make this easy. This is the inherent cost of "replace" mechanisms noted in the overview.
Page layouts: an OPT-IN template in the page form's dropdown
The configurator page has a second, gentler mechanism besides replacing its default.php: put an
additional template file in
getDirCustomization()/templates/configuratorpage/<name>.php e.g. betacalco.php
and <name> appears as a choice in the page form's "Template" dropdown
(ConfigboxModelTemplates::getConfiguratorPageTemplates() scans the folder; file names containing
_ are treated as partials and skipped). The page record's layoutname picks it, resolved in
ConfigboxViewConfiguratorpage::display().
This is how a redesign ships without touching anything else: pages on layout default keep the
stock template, and only the pages an admin explicitly switches use yours. Base the file on the
CURRENT views/configuratorpage/tmpl/default.php (same override tax as above), wrap your markup in
a marker class the layout's CSS/JS scope to, and let everything else — question rendering, the
#configurator-data payload, the finish button — come through unchanged. Worked examples in the
cbx-joomla site customization: accordion.php (a wizard flow) and betacalco.php (a brand design
with a live product-code bar).
3. Overriding a view class (changing the data)
When you need different data on a screen — load an extra related record, change which model is used, alter the page-title logic, add a variable for your template — override the view class.
3.1 The resolution — and an asymmetry to know about
KenedoView::getView() (:181-226) builds the same two candidates as the controller/model loaders:
$regularPath = KenedoPlatform::p()->getComponentDir($component).'/views/'.$viewName.'/view.html.php';
$customPath = KenedoPlatform::p()->getDirCustomization() .'/views/'.$viewName.'/view.html.php';
if (is_file($regularPath)) { $path = $regularPath; } // ← regular checked FIRST
elseif (is_file($customPath)) { $path = $customPath; }
⚠️ Asymmetry — view classes do not follow "customization wins", but templates do.
getView()checks the regular (core) path first (:202-207), so adata/customization/views/<view>/view.html.phpis only used when no core view of that name exists — i.e. for brand-new views you add, not for shadowing an existing core view. (Controllers and models behave the same way — core-first, new-only; seecom_configbox_overriding_controllers_and_models.md.) The asymmetry that bites is template vs. view class: a customization template (§2.1) shadows core, but a customization view class does not.Consequences:
- To add a new screen, put its view class under
data/customization/views/<newview>/view.html.php— it loads because there is no core<newview>.- To change an existing core view's data, you cannot shadow it via the customization
views/folder. Use one of these instead: (a) a template override if markup is all you need; (b) add/tweak fields on the model viamodel_property_customization/(data shape — see the stock-models guide); (c) change the view's data or the controller behind it via asystem_overrides/class override; or (d) as a last resort, asystem_overrides/class override of the view itself (see the boot-hooks guide). Note that the customizationmodels/andcontrollers/folders will not help here (core-first, new-only). Confirm the precedence againstgetView()for your component version before relying on it.
3.2 Adding a new view (the supported customization views/ use)
A new view is a directory under data/customization/views/<view>/ containing view.html.php (the class)
and tmpl/<layout>.php (its template). The class name follows the framework convention
ConfigboxView<Viewname> (the component+view derivation in getComponentNameFromClass() /
getViewNameFromClass(), :259-265):
// data/customization/views/myreport/view.html.php
<?php
defined('CB_VALID_ENTRY') or die();
class ConfigboxViewMyreport extends KenedoView {
function display() {
// Prepare whatever data the template needs:
$this->assign('rows', ConfigboxMyreportHelper::getRows());
$this->assign('pageTitle', KText::_('CB_MY_REPORT'));
// Render via the standard template chain (slot order from §2.1):
$this->renderView(); // uses tmpl/default.php unless 'layout' says otherwise
}
}
// data/customization/views/myreport/tmpl/default.php
<?php
defined('CB_VALID_ENTRY') or die();
/** @var $this ConfigboxViewMyreport */
?>
<h1><?php echo hsc($this->pageTitle); ?></h1>
<table>
<?php foreach ($this->rows as $row): ?>
<tr><td><?php echo hsc($row->label); ?></td><td><?php echo hsc($row->value); ?></td></tr>
<?php endforeach; ?>
</table>
A new view usually needs a route to reach it (a controller action and/or a SEF/menu entry). See
../platform/joomla/com_configbox_sef_urls.md for giving a custom view its own clean URL (controller hooks + a
customview menu item delivered via migration), and the controllers/models guide for the controller side.
4. Deployment checklist
data/customization/
templates/<view>/<layout>.php ← override an existing screen's markup (the common case)
views/<newview>/view.html.php ← a brand-new view class (new screens only — see §3.1)
views/<newview>/tmpl/<layout>.php ← the new view's template
- Decide template vs. view. Markup only → template override (§2). Different data → model/controller override or new view (§3 and the controllers/models guide).
- Find the view name and layout name (§2.2): view = the
views/<dir>name; layout = thelayoutrequest keyword (defaultdefault). - Copy the core template up to
data/customization/templates/<view>/<layout>.phpand edit the copy. Keep the diff from core as small as possible. - Escape all output with
hsc(); read view-prepared data via$this->…. - For a new view, place the class + template under
data/customization/views/<newview>/and wire a route to it. - Verify manually — load the screen, and for XHR-injected admin forms confirm it renders after injection too, not only on full reload. CBX has zero automated tests.
- After each component update, re-diff any template override against the (possibly changed) core template so you don't ship a stale copy.
5. Conventions & gotchas
- Template precedence ≠ view-class precedence. Templates: customization beats core. View classes:
core beats customization (regular path first,
:202) — the customizationviews/slot is for new views only. This is the single most surprising thing in this area (§3.1). - Copy only the layout you change. A view can have several templates; overriding
default.phpdoes not affectnotfound.phpetc. $thisis the view, not the model. Read$this->record,$this->records,$this->properties,$this->pageTitle. The properties areKenedoPropertyobjects — render through them, don't hand-roll field HTML from raw columns where a property already does it.- Escape everything. No auto-escaping;
hsc()on every dynamic value. SQL is hand-built mysqli — never let raw request data reach a query from a view. - The layout name is sanitised, the view name is structural. You can't traverse paths via
layout(:474-475); the view name comes from the resolved view directory, not from request input. - Match the idioms. Plain PHP templates, no namespaces/PSR-4, static helpers. Don't introduce a templating engine or modern Joomla layout APIs into Kenedo views.
See also
com_configbox_customization_overview.md— the extension-point map and the resolution rule.com_configbox_overriding_controllers_and_models.md— change the data/behavior behind a screen (controllers and models are core-first too; override existing ones viasystem_overrides/).com_configbox_custom_properties.md— how form/list fields render (templates lean on property output).technical/com_configbox_kenedo_mvc.md— the view lifecycle (display→prepareTemplateVars→renderView).technical/com_configbox_kenedo_view.md—KenedoViewin depth: the layer under this guide — CRUD vs multi-purpose views, how a view controls its CSS/JS, curryable setters, and the.view-<viewname>CSS-isolation convention your override templates should follow.../platform/joomla/com_configbox_sef_urls.md— giving a new custom view its own clean URL.external/kenedo/classes/KenedoView.php—getView()(:181),renderView()(:459),prepareTemplateVars*()(:282,:295,:375).