yceruto
yceruto

Reputation: 9575

How can I override **partially** a third-party template?

I need to override a template from third-party bundle by following one of the Symfony's built-in conventions. The Symfony documentation talks about them:

To override the bundle template, just copy index.html.twig template from the bundle to app/Resources/AcmeBlogBundle/views/Blog/index.html.twig (the app/Resources/AcmeBlogBundle directory won't exist, so you'll need to create it). You're now free to customize the template.

You can also override templates from within a bundle by using bundle inheritance. For more information, see How to Use Bundle Inheritance to Override Parts of a Bundle.

While this approach may work, it can be overly complicated if you only need to override a small portion of the template (e.g. some blocks). Additionally, if the third-party bundle updates its own template, your version of the template may become outdated and require updates to stay current with the latest changes.

This is what I've tried to do without success:

{# app/Resources/AcmeBlogBundle/views/Blog/layout.html.twig #}
{% extends '@AcmeBlog/Blog/layout.html.twig' %}

{% block title %}My Default Title{% endblock %}

The above code doesn't work. It breaks after reaching the maximum execution time when I've accessed this page and the clear cache command never ends.

Why it doesn't works and how to achieve it without copying the entire parent template from the third-party bundle?


Related issues and pull-requests without workaround:

Upvotes: 3

Views: 818

Answers (1)

yceruto
yceruto

Reputation: 9575

Why it doesn't works?

{# app/Resources/AcmeBlogBundle/views/Blog/layout.html.twig #}
{% extends '@AcmeBlog/Blog/layout.html.twig' %}

{% block title %}My Default Title{% endblock %}

The reason comes from Twig's paths and namespaces auto-configuration between bundles, their children and Symfony's path convention. By default Symfony/Bundle/TwigBundle assign the same Twig's namespace (AcmeBlog) for all paths, following this order:

# config.yml
twig:
    paths:
        # Auto-configuration behind the scenes for TwigBundle extension:

        # (1st) if AcmeBlogChildBundle has as parent to AcmeBlogBundle
        'src/AcmeBlogChildBundle/Resources/views': AcmeBlog 

        # (2nd) Path to override bundles (Symfony's convention)
        'app/Resources/AcmeBlogBundle/views':      AcmeBlog 

        # (3rd) third-party bundle
        'vendor/acme/blog-bundle/Resources/views': AcmeBlog 

That means, if you need to render the @AcmeBlog/Blog/layout.html.twig template, Twig try to find it in all paths (in the order has been defined) whose namespace matches AcmeBlog.

So to override it, you had to create this template (/Blog/layout.html.twig) in (1st) or (2nd) paths. But, if you're extending from @AcmeBlog/Blog/layout.html.twig at the same time (thinking about extends from the original template) then Twig performs the same previous procedure, causing a circular template reference (infinite loop) and never reach the (3rd) path.

How to achieve it without copy the entire parent template from the third-party bundle?

Symfony's built-in workaround

Let's define a different Twig's namespace for this third-party bundle in twig paths configuration:

# config.yml
twig:
    paths:
        # (4th) third-party bundle alias.
        # The path can be relative to project or real path
        vendor/acme/blog-bundle/Resources/views: AcmeBlogOriginal

Later, use the namespace alias to avoid circular reference when you're overriding a third-party template that extends from the original:

{# app/Resources/AcmeBlogBundle/views/Blog/layout.html.twig #}
{% extends '@AcmeBlogOriginal/Blog/layout.html.twig' %}

{% block title %}My Default Title{% endblock %}

Thus, you're able to override blocks instead the whole template. Even should work out-of-the-box as it's about Twig's paths only.


Since Symfony 3.4 this issue has been solved as built-in feature. http://symfony.com/blog/new-in-symfony-3-4-improved-the-overriding-of-templates#overriding-and-extending-templates

Upvotes: 5

Related Questions