Symfony bundle inheritance and twig templates overriding - symfony

I have a third party bundle called VendorDeliveryBundle.
I want to override one of its twig templates that i called with this syntax in one of my App twig templates:
{% include '#VendorDelivery/Frontend/Booking/_delivery.html.twig' with { 'form': form } only %}
Like this it works, the vendor template is called.
But if i want to override this template by registering the overriding bundle in AppKernel (as described in https://symfony.com/doc/2.8/bundles/inheritance.html) and by creating:
App/DeliveryBundle/Resources/views/Frontend/Booking/_delivery.html.twig
this template doesn't override the vendor template.
But if i use this syntax instead
{% include 'VendorDeliveryBundle:Frontend:Booking/_delivery.html.twig' with { 'form': form } only %}
the template is overridden.
It seems that the # syntax doesn't work as expected.
So i'm wondering if it is a bug or a normal behavior considering this symfony documentation https://symfony.com/doc/2.8/bundles/inheritance.html:
The overriding of resources only works when you refer to resources with the #FOSUserBundle/Resources/config/routing/security.xml method. If you refer to resources without using the #BundleName shortcut, they can't be overridden in this way.
Thanks,

# in twig syntax is a feature called "namespaced paths". (documentation)
Use functionality of {% include 'Bundle:Folder:template' %} it is not the same thing as {% include '#Bundle\Folder\template' %}.
For sample, if you overriding fosub bundle:
{% include '#FOSUser/Security/login.html.twig' %} {# Will be fosub template #}
{% include '#User/Security/login.html.twig' %} {# Will be overrided template #}
{% include 'FOSUserBundle:Security:login.html.twig' %} {# Also will be overrided template #}
Also, i want to add, that if you want to override only a template (without global functionality as controllers, listeners, etc) you can added templates to you app directory. It is well described in this documentation

Related

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:
https://github.com/symfony/symfony/issues/1966 (2011)
https://github.com/symfony/symfony/pull/2202 (2011)
https://github.com/symfony/symfony-docs/issues/752 (2011) | Unrelated.
https://github.com/twigphp/Twig/issues/1334 (2014)
https://github.com/symfony/symfony/issues/15755 (2015)
https://github.com/symfony/symfony/issues/17054 (2015)
https://github.com/symfony/symfony/issues/17407 (2016)
https://github.com/symfony/symfony/issues/17557 (2016)
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

dynamic function in twig template

I would like to display some dynamic text on many twig-templates.
Instead of adding this variable to every Action , I would prefer to write it once and somehow add it to the templates like
{{ displaytext('ann') }}
How can I achieve this?
You can achieve this by creating a Twig Extension class.
Read more about this at http://symfony.com/doc/current/cookbook/templating/twig_extension.html
You can create a file with this variable and include it in the others files -> Twig includes
You can set variables for your included page as shown in the documentation:
{# template.html will have access to the variables from the current context and the additional ones provided #}
{% include 'template.html' with {'foo': 'bar'} %}
{% set vars = {'foo': 'bar'} %}
{% include 'template.html' with vars %}

Symfony Form Theme extend inline

I extend FosUserBundle register form like this:
{% extends "#App/base.html.twig" %}
{% form_theme form 'bootstrap_3_horizontal_layout.html.twig' %}
{% block form_label_class -%}
col-md-8
{%- endblock form_label_class %}
Unfortunately the block form_label_class which is from bootstrap_3_horizontal_layout.html.twig is not extending there.
I want now in my form to use label as col-md-8 (instead the default col-sm-2) but then in another form maybe want to use col-md-6 and so on.
Is there an easy way to do it inline in every from instead create extends for each col-md-XX which is really not very convenient at all.
Thanks a lot!
You need to tell Symfony to include your current template as a form theme:
{% form_theme form with [
'bootstrap_3_horizontal_layout.html.twig',
_self,
] %}
Otherwise, it doesn't know to look in the current template for form theme blocks.
You can also use this to include other templates with form blocks as well. This is helpful if you have multiple templates that re-use the same form blocks; you can refactor them all into one template and reference it everywhere it's needed.

twig symfony directory separator

I use twig in a personal project and would like to access to files as symfony sensiolab with ":" as directory separator. For example :
{% extends '::base.html.twig' %}
{% extends ':default:base.html.twig' %}
Just need to replace ":" by "/" without bundle..
Does someone know how to do this ?
The colons in the template inheritance structure are not to reference directories but resources, the normal format is:
{% extends 'bundle_name:controller:template'%}
The different combinations of the parts will tell Symfony where to find the resource, for example:
{% extends "::base.html.twig" %} means that the base.html.twig template is in the global app resources folder.
{% extends "AcmeDemoBundle::part.html.twig" %}
This means that the template is located inside the Resources folder of the AcmeDemo Bundle.

Nesting included Twig templates?

I'd like to pass the output of an included Twig template to another included Twig template as a parameter, like so:
{% include 'MyBundle:Default:tpl1.html.twig' with {'item': include 'MyBundle:Default:tpl2.html.twig'} %}
Unfortunately, this does not work as the syntax is invalid.
Any ideas how to nest templates like this / store the output of an included template in a variable?
Or is there an alternative way to accomplish what I want to do? I thought about defining blocks in the included template, but it does not seem to be possible to overwrite them from the "outer" template ...
Try settings the template's content in a variable:
{% set content %}
{% include 'foo' %}
{% endset %}
{% include 'bar' with {'item': content } %}
It should work.

Resources