Skip to content
Nibiru docsv0.9.2

Views & Smarty

How .tpl templates are resolved, the View::assign pipeline, caching, and shared partials.

Stable Reading time ~ 3 min Edit on GitHub

Views in Nibiru are Smarty 3 templates. Each controller has a default template; nested actions can have their own. The View singleton wraps the Smarty engine and exposes one global helper: View::assign().

application/view/
├── templates/ # source .tpl files (you write these)
│ ├── index.tpl # → indexController::pageAction()
│ ├── products.tpl # → productsController::pageAction()
│ ├── products/
│ │ └── detail.tpl # → productsController::detailAction()
│ ├── shared/
│ │ ├── header.tpl
│ │ ├── footer.tpl
│ │ └── meta.tpl
│ ├── navigation.tpl
│ └── pageination.tpl # (note the spelling)
├── templates_c/ # Smarty compile cache (auto)
├── cache/ # rendered HTML cache (when caching=true)
└── mockup/ # static design mockups

templates_c/ is created and managed by Smarty. It must be writable by your web server user. cache/ is only used when [ENGINE] caching = true.

The Display layer resolves the template path from the matched controller:

  • For pageAction()templates/<controller>.tpl.
  • For a named action fooAction()templates/<controller>/foo.tpl if it exists, otherwise templates/<controller>.tpl.

If neither resolves, the soft 404 error template is rendered.

{include 'shared/header.tpl'}
<body>
{include file="navigation.tpl"}
<main class="container">
<h1>{$title|escape}</h1>
<ul>
{foreach $products as $p}
<li><a href="/products/detail/{$p.id}">{$p.name|escape}</a></li>
{/foreach}
</ul>
</main>
{include 'shared/footer.tpl'}
</body>

{include 'shared/header.tpl'} and {include file="navigation.tpl"} are both valid Smarty include forms.

View::assign() is how PHP data reaches templates. It’s static, idempotent, and array-friendly:

View::assign(['title' => 'Products']);
View::assign([
'products' => $list,
'count' => count($list),
]);

Later calls overwrite earlier ones for the same key. Inside templates these become {$title}, {$products}, {$count}.

There’s no View::display() you call manually — the dispatcher invokes Display::display() automatically after pageAction() returns.

The convention used across the showcase apps is to push the configured asset list into the template:

View::assign([
'css' => Config::getInstance()->getConfig()[View::NIBIRU_SETTINGS]['smarty.css'],
'js' => Config::getInstance()->getConfig()[View::NIBIRU_SETTINGS]['smarty.js'],
]);
{* shared/header.tpl *}
<head>
<meta charset="utf-8">
<title>{$title|escape}</title>
{foreach $css as $stylesheet}
<link rel="stylesheet" href="{$stylesheet}">
{/foreach}
</head>

Then [SETTINGS] smarty.css[] in the INI file is the single source of truth for stylesheets.

Caching is opt-in:

[ENGINE]
caching = true

When enabled, Smarty caches the rendered HTML in application/view/cache/ with the lifetime Smarty::CACHING_LIFETIME_CURRENT (default ≈ 1 hour). Clear the cache with:

Terminal window
./nibiru -cache-clear

This wipes both templates_c/ and cache/ safely.

Things you’ll reach for daily:

{* variables *}
{$user.name}
{$products[0].title}
{* iteration *}
{foreach $items as $item}
...
{foreachelse}
No items.
{/foreach}
{* conditionals *}
{if $count > 0}…{else}…{/if}
{* string filters *}
{$body|escape} {* HTML escape *}
{$date|date_format:"%Y-%m-%d"}
{$price|string_format:"%.2f"}
{* includes *}
{include file="shared/header.tpl" title=$title}
{* assigning *}
{assign var="now" value=time()}

When an action calls View::forwardToJsonHeader(), Smarty is bypassed and Nibiru emits the assigned data key as JSON. Handy for AJAX endpoints:

public function searchAction() {
View::forwardToJsonHeader();
$results = $this->index->search($_REQUEST['q'] ?? '');
View::assign(['data' => $results]);
}

{include file="navigation.tpl"} reads from the JSON file configured in [SETTINGS] navigation. Production apps often load multiple named nav arrays:

public function navigationAction() {
JsonNavigation::getInstance()->loadJsonNavigationArray('headnavigation');
JsonNavigation::getInstance()->loadJsonNavigationArray('mainnavigation');
JsonNavigation::getInstance()->loadJsonNavigationArray('footer');
}

Each named array becomes a Smarty variable of the same name, ready to render in the matching partial.

  • templates_c/ permissions — if Smarty can’t write there, you’ll get a fatal error. Run ./nibiru -s once to fix.
  • {$variable} with no value — Smarty in default mode renders nothing rather than throwing. Turn on error_reporting = E_ALL and [ENGINE] debug = true while developing to spot typos.
  • HTML escaping — Smarty doesn’t auto-escape. Use |escape for any user-controlled string.