IDCT Bartosz Pachołek

Cookies Warning

This website uses cookies: We inform you that this site uses own, technical and third parties cookies to make sure our web page is user-friendly and to guarantee a high functionality of the webpage and for statistics tracking. In order to use some (contact form) parts of the website you need to accept the use of cookies.

Sylius themes: extend templates like a pro with exclamation mark

Published 2020-07-08

The problem

Official documentation of Sylius tells us that whenever we want to change a template we have few options: sylius theme events and a second, much more symfony-style way of providing override files using same directory structures as within bundles in which original files reside. Therefore, it suggests that if you want to change contents of @SyliusShopBundle/login.html.twig you should make a copy to templates/bundles/SyliusShopBundle/login.html.twig and place your updated contents there.

As a proof that I am not lying that somebody officially suggested such approach: Sylius 1

This text can be found under this url: https://docs.sylius.com/en/latest/customization/template.html

Now imagine a situation in which you have change a lot of files, while mostly making some minor adjustments and an major update of Sylius happened which although does not change the template blocks, but for example some fields' names in multiple files. If that is hard to imagine then be sure to know that an update like that actually happened: https://github.com/Sylius/Sylius/commit/080c7d6eef9f5e788a745b0da706cf55f197d646#diff-04120f8acd7aa621c497ab95171502cf

In a scenario in which you have the file Sylius/Bundle/UiBundle/Resources/views/Form/imagesTheme.html.twig overwritten in your application because of some additional contents in the blocks then the javascript which handles adding of new images would cease to work as now relies on previously not present attribute data-prototype-name.

Sounds bad right? So how to at least reduce the risk of such scenario?

The potential solution

The best or at least better case is to overwrite only parts which we actually need. Let us take the same file as in Sylius' docs as an example.

They say to copy the file @SyliusShopBundle/views/login.html.twig to templates/SyliusShopBundle/views/login.html.twig so let us do it.

If we go to http://localhost:8000/pl_PL/login (assuming that we are running the local dev server) we shall see the standard sylius' login screen which we all love: Sylius 2

Now imagine that we want to add a text "You are awesome!" under the form. According to the docs of Sylius we should put a code as follows into our file:

{% extends '@SyliusShop/layout.html.twig' %}

{% form_theme form '@SyliusShop/Form/theme.html.twig' %}

{% block content %}
    {% include '@SyliusShop/Login/_header.html.twig' %}

    {{ sylius_template_event('sylius.shop.login.after_content_header') }}

    <div class="ui padded segment">
        <div class="ui two column very relaxed stackable grid">
            <div class="column">
                {{ sylius_template_event('sylius.shop.login.main_column', _context) }}
            </div>
            <div class="ui hidden vertical divider">
            </div>
            <div class="column">
                {{ sylius_template_event('sylius.shop.login.register_column', _context) }}
            </div>
        </div>
    </div>
    <h1>You are awesome!</h1> 
{% endblock %}

Simple, right? But now imaging that the function sylius_template_event has changed its name, or that events have changed their ids (for example sylius.shop.login.main_column to sylius.shop.most_important_column): you would need to manually track all those changes and actually update your template files.

Is there a better way?

The most natural way would be to extend the original file and just add what we need:

{% extends '@SyliusShop/login.html.twig' %}

{% block content %}
    {{ parent() }}
    <h1>You are awesome!</h1> 
{% endblock %}

Note the namespace change.

If you try this, clear the cache and reload the url you will most likely enter an infinite loop or receive error due to maximum execution time. Why is that? Because our file actually overrides @SyliusShop/login.html.twig so symfony falls into a loop of extending the same Twig file over and over.

The solution

There is one little gimmick, one trick which solves the problem, the solution is to add ! after the @, before bundle's name - in our case:

{% extends '@!SyliusShop/login.html.twig' %}

{% block content %}
    {{ parent() }}
    <h1>You are awesome!</h1> 
{% endblock %}

It informs Symfony to look for a next file which matches the file and to skip the first one, from our project.

Clear the cache and check it out.

Simple and nice, is it not? Such solution has some drawbacks: for instance you cannot add any additional markup, yet you can add more blocks or hide some existing ones in the parent template file.

Solution is perfect if parent file has numerous blocks and we just want to modify one or some of them or when we need to write something before or after a block: since we rely heavily on parent objects we reduce the risk of losing integrity.

I hope it helps! As always: in case of any problems or questions: let me know.