Control Flow Directives #
Control flow directives wrap an element in a conditional or loop. Only one control-flow directive can appear on a single element.
Use <s-template> when you want control flow without adding a wrapper element.
Directives #
-
s:if- Render when a condition is true. -
s:ifblock- Render when a child block override exists. -
s:unless- Render when a condition is false. -
s:isset- Render when a variable is set. -
s:empty- Render when a value is empty. -
s:notempty- Render when a value is not empty. -
s:foreach- Loop over an iterable. -
s:forelse- Loop with an empty fallback. -
s:while- Loop while a condition is true. -
s:times- Loop a fixed number of times. -
s:cache- Cache a rendered fragment by key. -
s:switch- Switch/case rendering. -
s:ifcontent- Render wrappers only if they contain output. -
s:try- Wrap output in a try block with optional finally. -
s:finally- Optional sibling for s:try cleanup.
Examples #
s:if #
Render the element only when the expression evaluates to true.
<div s:if="$isReady">Ready</div>
<div s:if="!$isReady">Loading...</div>
<!-- $isReady = true -->
<div>Ready</div>
s:unless #
Render the element only when the expression evaluates to false.
For more about empty/false checks, see Empty Checking.
<div s:unless="$isReady">Loading...</div>
<s-unless condition="$isReady">Loading...</s-unless>
<!-- $isReady = false -->
<div>Loading...</div>
<!-- $isReady = false -->
Loading...
s:ifblock #
Render the element only when a child template defines the named block.
For inheritance details, see Template Inheritance.
<aside s:ifblock="'sidebar'">
<section s:block="sidebar"></section>
</aside>
<s-ifblock name="sidebar">
<aside><section s:block="sidebar"></section></aside>
</s-ifblock>
<aside>
<section>Sidebar content</section>
</aside>
s:isset #
Render the element when the variable is set (not null and defined).
<div s:isset="$user">Welcome, <?= $user->name ?></div>
<s-isset value="$user">Welcome, <?= $user->name ?></s-isset>
<!-- $user->name = 'Alice' -->
<div>Welcome, Alice</div>
<!-- $user->name = 'Alice' -->
Welcome, Alice
s:empty #
Render the element when the value is empty.
For more about empty/false checks, see Empty Checking.
<div s:empty="$items">No items found</div>
<s-empty value="$items">No items found</s-empty>
<!-- $items = [] -->
<div>No items found</div>
<!-- $items = [] -->
No items found
s:notempty #
Render the element when the value is not empty.
For more about empty/false checks, see Empty Checking.
<div s:notempty="$items">Items available</div>
<s-notempty value="$items">Items available</s-notempty>
<!-- $items = ['A'] -->
<div>Items available</div>
<!-- $items = ['A'] -->
Items available
s:foreach #
Repeat the host element for every item in an iterable.
If you only want to repeat children without repeating a wrapper element, place s:foreach on an s-template fragment.
For full loop metadata details, see Loop Metadata.
<ul s:foreach="$items as $item">
<li><?= $item ?></li>
</ul>
<dl s:foreach="$stats as $label => $value">
<dt><?= $label ?></dt>
<dd><?= $value ?></dd>
</dl>
<ul s:foreach="$items as $item">
<li s:class="['first' => $loop->first, 'last' => $loop->last, 'odd' => $loop->odd]">
<?= $item ?> (<?= $loop->iteration ?> of <?= $loop->count ?>)
</li>
</ul>
<!-- $items = ['A', 'B'] -->
<ul>
<li>A</li>
</ul>
<ul>
<li>B</li>
</ul>
s:forelse #
Loop over items and fall back to an s:empty sibling when empty.
For full loop metadata details, see Loop Metadata.
For more about empty/false checks, see Empty Checking.
<ul s:forelse="$items as $item">
<li><?= $item ?></li>
</ul>
<div s:empty>No items found</div>
<ul s:forelse="$items as $item">
<li s:class="['odd' => $loop->odd, 'even' => $loop->even]">
<?= $item ?> (<?= $loop->iteration ?>)
</li>
</ul>
<div s:empty>No items found</div>
<!-- $items = [] -->
<div>No items found</div>
s:while #
Repeat the element while a condition remains true.
<div s:while="$poller->hasNext()">
<?= $poller->next() ?>
</div>
<s-while condition="$poller->hasNext()">
<?= $poller->next() ?>
</s-while>
s:times #
Repeat the element a fixed number of times.
<span s:times="3">*</span>
<span s:times="5 as $i">#<?= $i ?></span>
<s-times count="3"><span>*</span></s-times>
<s-times count="5 as $i"><span>#<?= $i ?></span></s-times>
s:cache #
Cache a fragment’s rendered output using a configured PSR-16 cache store.
s:cache is opt-in at engine setup time. Register the optional FragmentCache extension:
use Sugar\Core\Engine;
use Sugar\Extension\FragmentCache\FragmentCacheExtension;
$cache = new YourPsr16CacheStore(); // must implement Psr\SimpleCache\CacheInterface
$engine = Engine::builder()
->withTemplateLoader($loader)
->withExtension(new FragmentCacheExtension($cache, defaultTtl: 300))
->build();
Directive forms:
-
s:cache- auto key, default TTL fromnew FragmentCacheExtension(..., defaultTtl: ...) -
s:cache="'users-' . $userId"- explicit key, default TTL -
s:cache="['key' => 'users-' . $userId, 'ttl' => 60]"- explicit key + per-fragment TTL override
<section s:cache>
<h2>Popular items</h2>
<?= $expensiveHtml ?>
</section>
<section s:cache="'users-' . $userId">
<?= $userCardHtml ?>
</section>
<section s:cache="['key' => 'users-' . $userId, 'ttl' => 120]">
<?= $userCardHtml ?>
</section>
If no fragment cache store is configured, s:cache is treated as a no-op wrapper and content still renders.
The element form uses a key attribute instead of the directive expression. Omitting key uses the same auto-key behaviour as bare s:cache:
<s-cache key="'sidebar'">
<nav>...</nav>
</s-cache>
<!-- Auto key -->
<s-cache>
<section>...</section>
</s-cache>
s:switch #
Choose between s:case and s:default children based on a value.
<div s:switch="$role">
<span s:case="'admin'">Administrator</span>
<span s:default>User</span>
</div>
<div s:switch="$status">
<span s:case="'open'">Open</span>
<span s:case="'closed'">Closed</span>
<span s:default>Unknown</span>
</div>
<div s:switch="$role">
<span s:case="'admin'">Administrator</span>
<span s:case="'moderator'">Moderator</span>
<span s:default>User</span>
</div>
s:ifcontent #
Render the wrapper only when it would contain output.
<div s:ifcontent class="card">
<?php if ($showContent): ?>
<p>Some content here</p>
<?php endif; ?>
</div>
s:try / s:finally #
Wrap output in a try block with an optional finally sibling. There is no s:catch directive; if s:finally is omitted, Sugar emits a catch that returns null to keep the PHP valid and silently stop output on errors.
<div s:try>
<?= $content ?>
</div>
<div s:finally>
<?php $logger->flush(); ?>
</div>
<s-try>
<?= $content ?>
</s-try>
<div s:finally>
<?php $logger->flush(); ?>
</div>