Guide 02 · Developer docs

Template languages: Smarty and Twig

Core dna renders server-side sites with Smarty or with Twig, one engine per site, and treats the two as peers. This guide is the one place both are documented: what they share, each engine's language and the platform's additions and limits, the two side by side, and translation.

L1–L2
Unit 01 · The shared model

Rendering a template from a string

Template code typed into the admin, in jobs, discount conditions, shipping methods and search facets, is rendered by the site's engine from a string rather than a file. Where the platform does it, the variables each place supplies, what happens when the code fails, how it differs from a file, and the review it needs.

Who this is for. The developer who writes template code in an admin field rather than in the repository, and whoever approves that code. Afterwards you will know which variables your code has, why it cannot always do what a template file does, where its errors go, and why changes to it deserve the same review as a commit.

A template that is not a file

Both engines can render a template handed to them as a string instead of a path. The platform uses this wherever an admin screen accepts template code. The string is rendered by the site's engine, so a Smarty site's template code is Smarty and a Twig site's is Twig, with the same syntax, the same platform helpers and, on Smarty, the same security policy as the templates in the repository.

Where the platform renders strings

WhereWhat the code receivesWhat the code is forWhen it fails
Jobs (Orchestration): template conditions, loops, and action fields that take template codehook (the job; the variable keeps the module's old name), batch (the job run), event, site, runData, customData, siteData, query; inside a loop also loopElement and loopRecord; a condition also resultBuilding a request body, record data or a value; deciding a branchThe action is marked as an error, with the message in its output in the job run's log
Discounts: template conditionsitems, cart, checkout, rule, discount, site, user, siteData, query, request, resultDeciding whether the discount applies, by calling result's setMatching(true)The condition does not match, and the error is logged
Shipping: template methodsfulfillment, site, user, siteData, query, ratesCreating shipping ratesNo rates are offered for the method, and the error is logged
Search: the facets template of the product indexindex, productDescriptionAdding facet values for a productThe product stays indexed without its custom facets, and the error is logged
Mailer emails, on a Twig site onlyThe email's merge dataThe subject and body of a mail-outThe platform falls back to plain placeholder substitution (below)

The underscore helpers of a page render (_request, _query, _templateContext and the rest) are not given to template code: each place passes its own short list, as above, and the code sees nothing else. Code copied from a page template that uses $_query therefore fails here; use query, where the list has it. Guide 9 owns the job side: Job action reference lists which fields take template code and what each must output.

How it differs from a template file

  • Twig template code cannot include or extend a file. The string is the only template Twig is given, so {% include 'partials/address.html' %} fails with Template "partials/address.html" is not defined. Smarty template code can include a file from the site's templates by its full name.
  • It is not in the repository. It is part of the record that holds it, changes when an editor saves that record, takes effect at once with no deploy, and is not in your version history. A site's code is therefore partly in the admin; write down where.
  • It runs in the background as often as in a page request. A job or a search reindex has no visitor and no page, which is one reason the page helpers are absent.
  • Output usually feeds a machine. A job's request body must be valid JSON once rendered, so every value goes through |json_encode in either engine, never into the JSON by hand.
Template code A job's HTTP request body
{$payload = [
  'id'    => $event->getRecord()->getId(),
  'title' => $event->getRecord()->getName(),
  'site'  => $site->getName()
]}
{$payload|json_encode}
{{ {
  'id':    event.getRecord().getId(),
  'title': event.getRecord().getName(),
  'site':  site.getName()
}|json_encode }}

The record an event carries, and how to read it, depend on the event: Job event reference has them. Building the whole value as an array and encoding it once, as above, is what keeps quotes and line breaks in a title from breaking the JSON.

The one strict render

Everywhere else, Twig renders template code with strict variables off, as it renders files: an undefined variable prints nothing. Mailer emails on a Twig site are the exception. Their subject and body are rendered with strict variables on, after the old %placeholder% tokens are turned into Twig variables. If any variable is undefined, the render fails and the platform sends the email with plain placeholder substitution instead, so the mail still goes out, but without whatever the Twig in it was meant to do. On a Smarty site the subject and body are not rendered as templates at all: only the placeholders are replaced.

Reviewing template code

Template code in the admin has the reach of a template in the repository, with fewer of the checks: no pull request, no deploy, no history in Git. Treat permission to edit these records as permission to change the site's code, and give it to the people you would let commit. On a Twig site there is no function list behind it either (Twig security), which raises the stakes of that decision. Before approving a change, check:

  • Who changed it, and is that person a developer? The record's revision or change history says who saved it.
  • What objects does it touch, and only to read? Code that should decide a discount has no reason to call anything on user beyond reading.
  • Is every value encoded for where it goes? |json_encode into JSON, |escape into HTML, never concatenated by hand.
  • Is there anything secret in it? Keys and passwords do not belong in template code, where every editor of the record can read them.
  • What happens when it fails? Look at the table above: a failed discount condition does not apply, and the error is logged.
Go deeper

Was this page helpful?

Your answer goes to the docs team.