Views & Smarty
How .tpl templates are resolved, the View::assign pipeline, caching, and shared partials.
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().
Where templates live
Section titled “Where templates live”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 mockupstemplates_c/ is created and managed by Smarty. It must be writable by your web server user. cache/ is only used when [ENGINE] caching = true.
Resolution rules
Section titled “Resolution rules”The Display layer resolves the template path from the matched controller:
- For
pageAction()→templates/<controller>.tpl. - For a named action
fooAction()→templates/<controller>/foo.tplif it exists, otherwisetemplates/<controller>.tpl.
If neither resolves, the soft 404 error template is rendered.
A minimal template
Section titled “A minimal template”{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
Section titled “View::assign”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.
Shared CSS/JS injection
Section titled “Shared CSS/JS injection”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
Section titled “Caching”Caching is opt-in:
[ENGINE]caching = trueWhen enabled, Smarty caches the rendered HTML in application/view/cache/ with the lifetime Smarty::CACHING_LIFETIME_CURRENT (default ≈ 1 hour). Clear the cache with:
./nibiru -cache-clearThis wipes both templates_c/ and cache/ safely.
Smarty essentials
Section titled “Smarty essentials”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()}JSON responses
Section titled “JSON responses”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]);}Navigation includes
Section titled “Navigation includes”{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.
Common gotchas
Section titled “Common gotchas”templates_c/permissions — if Smarty can’t write there, you’ll get a fatal error. Run./nibiru -sonce to fix.{$variable}with no value — Smarty in default mode renders nothing rather than throwing. Turn onerror_reporting = E_ALLand[ENGINE] debug = truewhile developing to spot typos.- HTML escaping — Smarty doesn’t auto-escape. Use
|escapefor any user-controlled string.