Flag of Ukraine
SymfonyCasts stands united with the people of Ukraine

Themes & Overriding Templates

Video not working?

It looks like your browser may not support the H264 codec. If you're using Linux, try a different browser or try installing the gstreamer0.10-ffmpeg gstreamer0.10-plugins-good packages.

Thanks! This saves us from needing to use Flash or encode videos in multiple formats. And that let's us get back to making more videos :). But as always, please feel free to message us.

We can now add a lot of dynamic content to our site, like these static blocks up here, grids, or lists. The grids and lists can hold items from Contentful or our Recipe entity. But to really make our site shine, we need flexibility over how these pieces look. Let's start simple, by overriding the template that renders what the "Title" block looks like for our entire app.

Finding Block Templates in the Profiler

To do that... we first need to figure out which template is currently responsible for rendering this block. An easy way to find out is to go to a page that renders one of these, refresh, and click on the Twig icon on the web debug toolbar. Down at the bottom, we see the whole tree. And if we look closely, ah ha! Apparently there's a template called block/title.html.twig!

Layouts itself also has a really nice web debug toolbar section. If you go to "Rendered blocks", it shows "Block definition: title", "Text", "List", and "Footer". And, as we saw, the Title is rendered by title.html.twig.

Hello Themes

Notice that almost all of these templates are nestled inside themes/standard/ directories. Layouts has a concept of themes, though we won't need to create multiple themes unless we're building some sort of multi-site application. In our case, we're just going to use the one built-in theme called standard.

But themes are still important, because anything inside of a theme can be easily overridden by putting a template in just the right location. We're going to use that convention to override the Title template.

Overriding the Title Template

Let's do it! First, in the templates/ directory, make sure you have an nglayouts/ subdirectory. Inside of that, add a new one called themes/... followed by a another subdirectory called standard/. You may have noticed that we're matching the structure that's over here: nglayouts/themes/standard/.

Inside of this, since the target template is named block/title.html.twig, if we create that same path, our title.html.twig will win. Do it: add another directory called block/ and a new file inside: title.html.twig. To see if it works, just write some dummy text:

Let's try this thing! Go back to the Skills page, refresh, and... absolutely nothing happens. That's because the first time we create this themes/ directory, we need to clear the cache.

php bin/console cache:clear

Do that... then with that behind us, try the page again. Woohoo! We now control how the Title block renders! The power!

Making the Title Template More Realistic

Okay, but even if we want to customize how the Title renders... we probably don't want to start from scratch. It would be better to reuse part of the core template, or at least use it as a reference.

Hit Shift+Shift, search for title.html.twig, and select "Include non-project items". Open the core one from nglayouts/themes/.

Wow. There is a lot going on here... including the fact that this extends another template: block.html.twig. Open that up.

This contains a lot of base functionality, like reading the dynamic css_class variable, which contains any CSS classes we enter into the admin. It also handles if there's a container or not. That's useful stuff!

In title.html.twig, it has code for whether or not the title is a link and other stuff. We could totally replace this template and ignore all this if we wanted to. But instead, copy the core template, paste it into our version:

{% extends '@nglayouts/block/block.html.twig' %}
{% import '@NetgenLayouts/parts/macros.html.twig' as macros %}
{% set tag = block.parameter('tag').value|default('h1') %}
{% set link = block.parameter('link') %}
{% block content %}
{# Located inside the "content" block to include the context from the parent template #}
{% set title = macros.inline_template(block.parameter('title').value, _context) %}
<{{ tag }} class="title">
{% if block.parameter('use_link').value and not link.empty %}
{{ nglayouts_render_parameter(link, {content: title}) }}
{% else %}
{{ title }}
{% endif %}
</{{ tag }}>
{% endblock %}

And just to prove that we can, let's remove that title class:

... lines 1 - 7
{% block content %}
... lines 9 - 11
<{{ tag }}>
... lines 13 - 17
</{{ tag }}>
{% endblock %}

Cool! Now go over, refresh and... it goes back to how it looked before. But down here, that title class on the <h1> is gone!

So the simplest way to control how something looks is to find the template that renders it and override it completely using this themes/ directory structure.

Let's use that trick again next to customize what it looks like when you render an "asset" field from Contentful, like this skill image field. But along the way, we're going to deep dive into a some massively important concepts: block views and view types.

Leave a comment!

0
Login or Register to join the conversation
Cat in space

"Houston: no signs of life"
Start the conversation!

What PHP libraries does this tutorial use?

// composer.json
{
    "require": {
        "php": ">=8.1.0",
        "ext-ctype": "*",
        "ext-iconv": "*",
        "babdev/pagerfanta-bundle": "^3.7", // v3.7.0
        "doctrine/doctrine-bundle": "^2.7", // 2.7.0
        "doctrine/doctrine-migrations-bundle": "^3.2", // 3.2.2
        "doctrine/orm": "^2.13", // 2.13.3
        "easycorp/easyadmin-bundle": "^4.4", // v4.4.1
        "netgen/layouts-contentful": "^1.3", // 1.3.2
        "netgen/layouts-standard": "^1.3", // 1.3.1
        "pagerfanta/doctrine-orm-adapter": "^3.6",
        "sensio/framework-extra-bundle": "^6.2", // v6.2.8
        "stof/doctrine-extensions-bundle": "^1.7", // v1.7.0
        "symfony/console": "5.4.*", // v5.4.14
        "symfony/dotenv": "5.4.*", // v5.4.5
        "symfony/flex": "^1.17|^2", // v2.2.3
        "symfony/framework-bundle": "5.4.*", // v5.4.14
        "symfony/monolog-bundle": "^3.0", // v3.8.0
        "symfony/proxy-manager-bridge": "5.4.*", // v5.4.6
        "symfony/runtime": "5.4.*", // v5.4.11
        "symfony/security-bundle": "5.4.*", // v5.4.11
        "symfony/twig-bundle": "5.4.*", // v5.4.8
        "symfony/ux-live-component": "^2.x-dev", // 2.x-dev
        "symfony/ux-twig-component": "^2.x-dev", // 2.x-dev
        "symfony/validator": "5.4.*", // v5.4.14
        "symfony/webpack-encore-bundle": "^1.15", // v1.16.0
        "symfony/yaml": "5.4.*", // v5.4.14
        "twig/extra-bundle": "^2.12|^3.0", // v3.4.0
        "twig/twig": "^2.12|^3.0" // v3.4.3
    },
    "require-dev": {
        "doctrine/doctrine-fixtures-bundle": "^3.4", // 3.4.2
        "symfony/debug-bundle": "5.4.*", // v5.4.11
        "symfony/maker-bundle": "^1.47", // v1.47.0
        "symfony/stopwatch": "5.4.*", // v5.4.13
        "symfony/web-profiler-bundle": "5.4.*", // v5.4.14
        "zenstruck/foundry": "^1.22" // v1.22.1
    }
}
userVoice