Yonel Ceruto
Contributed by Yonel Ceruto in #24179 , #24264 and #24064

In Symfony 3.4 we've deprecated bundle inheritance and in Symfony 4.0 we no longer recommend to use bundles for your own code, but overriding templates from third-party bundles is still a common need. That's why in Symfony 3.4 we've improved the overriding of templates in preparation for Symfony 4.

New overriding directory

In Symfony 2 and 3 applications, third-party bundle templates were overridden in the app/Resources/<BundleName>/views/ directory. In Symfony 4 this directory would have been moved to src/, which doesn't look nice to store templates now that we have a templates/ directory at the project root.

Therefore, in Symfony 3.4 we've created a new directory to override bundle templates: templates/bundles/<BundleName>/. For example, if you want to customize error pages in a Symfony application:

1
2
3
4
5
6
7
{# Symfony 3.3 #}
{# app/Resources/TwigBundle/views/Exception/error404.html.twig #}
Page Not Found!

{# Symfony 3.4 #}
{# templates/bundles/TwigBundle/Exception/error404.html.twig #}
Page Not Found!

Overriding and extending templates

Sometimes you want to override a third-party bundle template but reuse most of its contents, to avoid code duplication. Imagine that you want to override and extend the layout.html.twig template from FOSUserBundle:

1
2
3
{# templates/bundles/FOSUserBundle/layout.html.twig #}
{% extends '@FOSUser/layout.html.twig' %}
{# ... this doesn't work ... #}

If you try this example, you'll see "reached nested level" error because the simultaneous overriding and extending is similar to an infinite loop. In Symfony 3.4 we solved this problem creating a new and exclusive Twig namespace for each bundle.

The new namespace is the same as before but adding ! before the bundle name. In this case, @FOSUser refers to the normal Twig namespace which can include third-party templates and your own overridden templates and @!FOSUser refers exclusively to the templates defined by the third-party bundle (no matter if they have been overridden, you always get the original templates).

Using this new namespace, it's trivial to solve the previous problem:

1
2
3
{# templates/bundles/FOSUserBundle/layout.html.twig #}
{% extends '@!FOSUser/layout.html.twig' %}
{# ... this works as expected ... #}

Better debug:twig command

Finally, to make these changes easier to debug, the debug:twig command now displays the full list of Twig namespaces and their associated file paths in the same prioritized order used by Symfony.

Published in #Living on the edge