11/30/2015

Extending a parent Twig block with parent()

Twig blocks behave like methods on a class. A child template that extends a parent and then defines a block of the same name replaces the parent's version entirely, in the same way an overridden method replaces the one it overrides.

That is usually fine for a content block and almost never what you wanted for a block holding stylesheets or scripts, because the parent's are gone the moment the child defines its own. parent() puts them back:

{% extends 'base.html.twig' %}

{% block stylesheets %}
    {{ parent() }}
    <link rel="stylesheet" href="{{ asset('css/checkout.css') }}">
{% endblock %}

Without the parent() call, that child page loads checkout.css and nothing else — every stylesheet the base template declared is silently dropped. The symptom is a page that looks unstyled for no visible reason, on one route only.

The template name

'base.html.twig' is a path relative to templates/. If you are reading an older example that says something like MainBundle::Base:main.html.twig, that is Symfony 2 bundle notation and it was removed in Symfony 4.0. Templates inside a bundle now use @BundleName/path/to/template.html.twig, and templates in your own application are just their path under templates/.

Where it also helps

parent() works in any block, not only the asset ones. A base template that sets a default page title is the other common case:

{# templates/base.html.twig #}
{% block title %}Belchamber Tools{% endblock %}
{# templates/article/show.html.twig #}
{% block title %}{{ article.title }} - {{ parent() }}{% endblock %}

One place it does not work is a block defined in the same template rather than inherited — parent() needs a parent block to reach for, and calling it without one is a Twig error rather than an empty string.