Skip to content
liseli edited this page Mar 2, 2026 · 3 revisions

Template Rendering - Smarty 5

We upgraded the catalog template to Smarty 5, which enforces namespaced APIs and protected property access. This page details the template structure and flow, and also how the template should be instantiated to avoid the “class not found” or “Cannot access protected property” errors that break bootstrapping.

Smarty documentation: https://smarty-php.github.io/smarty/5.x/

Template Structure

interface/
├── themes/
│   ├── firebird/
│   │   ├── Record/ # Record display
│   │   ├── Search/ # Search display
│   │   ├── layout.tpl          # Master layout
│   │   ├── search_form.tpl          # Search form
│   │   ├── footer.tpl  # footer display
│   │   ├── header.tpl          # header display
│   │   └── ...
└── compile/                    # Compiled templates (cached)

Rendering flow example

  1. The controller assigns the data the template needs, e.g., when rendering Record/cite.tpl:

    $interface->assign('record', $record);  // line 149 `services/Record.php`
    
  2. The controller tells Smarty which specific page template to render:

     $interface->setTemplate('view.tpl');
    
  3. The controller renders the shared master layout:

    $interface->display('layout.tpl');
    
  4. layout.tpl includes the chosen page template:

    {include file=$pageTemplate}
    
  5. Any template referenced this way (e.g., Record/cite.tpl) has access to the assigned variables and globals:

    {$record}
    {$citeFormat}
    {$configArray}
    {$session}

    Assign new variables in the controller via assign() before calling setTemplate()/display().

The controller’s launch() method (e.g., services/Record/Cite.php::launch) is the place where this flow happens. launch() is invoked after the framework has assembled the request context; it assigns every piece of data the resulting page needs, calls setTemplate()/setPageTitle()/setTemplate() if necessary, and finally either display()s layout.tpl (for a full HTML response) or fetch()es a single template in cases such as the lightbox citation preview. Think of launch() as the orchestrator: it populates the Smarty context, points Smarty at the correct page template, and renders the master layout that includes $pageTemplate, so templates never reach into Smarty internals for data or template selection.

UInterface bootstrap flow (see sys/Interface.php)

  1. vendor/autoload.php is required so Composer loads Smarty 5 and any registered plugins.

  2. The constructor calls the setter APIs to:

    • Point template_dir at the active theme ($local/interface/themes/$theme).
    • Ensure the compile directory exists (interface/compile/$theme), then call setCompileDir.
    • Set the cache directory, caching mode, debugging flag, and compile-check flag via the named setter methods.
  3. Legacy plugins under interface/plugins are loaded by registerLegacyPlugins(), which now includes each plugin file and registers it by name with registerPlugin().

  4. JSON-encoding and array counting are explicitly re-registered as 'modifier' plugins. Modifiers are used to transform the output of a variable.

  5. Common site metadata (site, path, url, etc.) is assigned via assign() so templates can reference them without touching Smarty’s internals.

$this->assign('site', $configArray['Site']);
$this->assign('path', $configArray['Site']['path']);
$this->assign('url', $configArray['Site']['url']);

If you add new plugins, register them explicitly (either by calling registerPlugin() directly or extending registerLegacyPlugins() to discover them). Smarty 5 no longer loads entire directories automatically.

Namespace expectations

  • Everywhere we instantiate Smarty, we now import or reference Smarty\Smarty.
    • The shared UI class in sys/Interface.php starts with use Smarty\Smarty; and class UInterface extends Smarty to inherit the rendering helpers and plugin APIs.
    • Any standalone scripts (for example static/api/volumes.php) must either use Smarty\Smarty; themselves or call new \Smarty\Smarty(); a bare new Smarty() will fail because the class no longer lives in the global namespace.
  • Tests or helpers that assert the type of the template engine should also rely on Smarty::class.
$this->assertTrue(class_exists(Smarty::class), 'Smarty\\Smarty class should be available');

Setter-based configuration

Smarty 5 protects configuration properties (e.g., $template_dir, $compile_dir, $cache_dir, $caching). All directory or mode changes must go through the provided setters:

Task Preferred API
Set template directory $interface->setTemplateDir(...)
Set compile directory $interface->setCompileDir(...)
Set cache directory $interface->setCacheDir(...)
Enable/disable caching $interface->setCaching(Smarty::CACHING_OFF) or set to another constant
Control debug/compile checks $interface->setDebugging(false) / $interface->setCompileCheck(Smarty::COMPILECHECK_ON)

Avoid accessing any internal property directly (->template_dir, ->compile_dir, etc.). If you discover a legacy assignment, migrate it to the matching setter.

Clone this wiki locally