How-to library · Developer docs

How-to library

Task-shaped pages: a title that starts with a verb, the situation in a few lines, the steps, the working code in both engines, and why this way rather than another. Found from a search, done, and left.

Pass a variable across an inheritance boundary

An assignment at the top of a child template, outside any block, reaches the parent everywhere. An assignment inside a block does not: Smarty sees it only after the block, Twig never. For components, use the template context.

Context

Context. A page template needs to hand the base template a title, a body class or a flag. Where the assignment sits decides whether it arrives. This was checked by rendering a child and parent pair through the platform's own Smarty and Twig, and the result is the same rule for both engines with one difference in the block case.

Steps

  1. Assign the variable in the child outside any block, right after the extends tag. The parent sees it everywhere, including before its first block.
  2. Do not rely on an assignment inside a block. In Smarty it is visible to the parent only after that block renders, and in Twig it stays inside the block.
  3. For a value that must reach a component rendered with getHtml(), or an include, put it in _templateContext, which every template in the render shares.

Code

Template code
{* child: pages/pages/article.html *}
{extends 'includes/frontend/base-template.html'}
{$bodyClass = 'article'}                      {* reaches the parent everywhere *}
{block name='content'}
  {$readingTime = 4}                           {* parent sees this only after this block *}
  ...
{/block}
{# child: pages/pages/article.html #}
{% extends 'includes/frontend/base-template.html' %}
{% set bodyClass = 'article' %}                {# reaches the parent everywhere #}
{% block content %}
  {% set readingTime = 4 %}                    {# stays inside the block #}
  ...
{% endblock %}

And the parent, with what each line receives:

Template code
{* parent: includes/frontend/base-template.html *}
<body class="{$bodyClass|default:''}">      {* article *}
{block name='content'}{/block}
<footer>{$readingTime|default:''} min</footer>  {* 4, because it comes after the block *}
{# parent: includes/frontend/base-template.html #}
<body class="{{ bodyClass|default('') }}">  {# article #}
{% block content %}{% endblock %}
<footer>{{ readingTime|default('') }} min</footer>  {# empty #}
Template code
{* any template, any depth: *}
{$_templateContext->set('bodyClass', 'article')}
{* the base template: *}
<body class="{$_templateContext->get('bodyClass', '')}">
{# any template, any depth: #}
{% do _templateContext.set('bodyClass', 'article') %}
{# the base template: #}
<body class="{{ _templateContext.get('bodyClass', '') }}">

Why this and not that

Not Smarty's scope='parent' inside a block: it behaves like the plain assignment above, visible only after the block, so it solves nothing here and does not exist in Twig. Not scope='global': it works in Smarty, does not port, and is hard to trace. The template context is the one mechanism that behaves identically in both engines and also reaches components, which no assignment does.

Go deeper

Was this page helpful?

Your answer goes to the docs team.