Twig - render block from included or parent template - php

is there any way to render block from included or parent template?
I have 3 templates
main.html.twig:
{% include 'navbar.html.twig' %}
<div id="main_content">
{% block application_content %}
{% endblock application_content %}
</div>
{% block application_footer %}
footer
{% endblock application_footer %}
navbar.html.twig:
<p>bla bla</p>
{% block navbar_profile_photo %}
<img src="{{ image }}">
{% endblock navbar_profile_photo %}
content.html.twig:
{% extends "main.html.twig" %}
{% block application_content %}
lorem ipsum
{% block foo %}
dolor sit amet
{% endblock foo %}
{% endblock application_content %}
I want to be able call something like this
$twig->loadTemplate('content.html.twig')->renderBlock('navbar_profile_photo', array('image' => 'bar.jpg'))
But I can get only blocks from 'content.html.twig'
$twig->loadTemplate('content.html.twig')->getBlockNames();
returns
['application_content', 'foo']
I could add to content.html.twig
{% use 'navbar.html.twig' %}
but in my case I had to do it in many templates with many different use statements
Is there some way to get the final template with all includes and extends and blocks?

In content.html.twig file you must add:
{% extends "EverlutionApplicationBundle::main.html.twig" %}
{% block navbar_profile_photo %}
{{ parent() }}
{% endblock navbar_profile_photo %}
.....

Related

Twig: Pass Twig Markup to an Include

I want to pass a macro(icon()) to a nested template of mine, through passing the markup as text.
But due to the |raw filter, Twig wont render the markup as Twig. Is there a way to achieve this? Basically I get this rendered {{ icon("phone-call") }}
main.html.twig
{% embed "Block/context_box_column_lane.html.twig" with {
'title': 'Test',
'subtitle': 'Subtitle',
'text': '
<p class="context-box-text">{{ icon("phone-call") }}</p>
'
} %}{% endembed %}
context_box_column_lane.html.twig
{% extends "Block/section_sidebar.html.twig" %}
{% block section_content %}
{% embed 'Block/context-box.html.twig' %}{% endembed %}
{% endblock %}
context-box.html.twig
{% from "#html/macros/icons.twig" import get as icon %}
<div class="b-context-box {% if has_border %}has-border{% endif %}">
{% if subtitle %}
<h3 class="section-subtitle">{{ subtitle|trans }}</h3>
{% endif %}
{% block content %}
{% if text is defined %}
{{ text|trans|raw }}
{% endif %}
{% endblock %}
</div>

Replace only one nested block in twig

I have the following twig structure:
base.twig
<html>
<head>
</head>
<body class="fade-in {{body_class}}">
<main>
{% block menu %}
{% include 'menu.twig' %}
{% endblock %}
</main>
</body>
</html>
menu.twig
<header>
<div>
{% block menu_main %}
{% include 'menu-main.twig' %}
{% endblock %}
{% block menu_country %}
{% include 'menu-country.twig' with { menu_country: dropdownland } %}
{% endblock %}
</div>
</header>
child.twig
{% extends "base.twig" %}
{% block menu %}
{% block menu_country %}
{% include 'menu-country.twig' with { menu_country: menu_ap_countries } %}
{% endblock %}
{% endblock %}
What i want to achieve is, just replace the block menu_country inside child.twig. If i use the approach above, the whole block menu gets replaced by only menu_country which means that the block menu_main is missing.
I tried also
{% extends "base.twig" %}
{% block menu %}
{{ parent() }}
{% block menu_country %}
{% include 'menu-country.twig' with { menu_country: menu_ap_countries } %}
{% endblock %}
{% endblock %}
but this renders the block menu_country twice. Is there any way to achieve this?
Thanks a lot.
After further investigation due to #DarkBees answer, i came accross the embed function which does exactly what i need in this case. So the extending template becomes this:
{% block menu %}
{% embed 'menu.twig'%}
{% block menu_country %}
{% include 'menu-country.twig' with { menu_country: menu_ap_countries } %}
{% endblock %}
{% endembed %}
{% endblock %}
By embedding, I am able to overwrite the block inside menu.twig
Including templates does not mean u are importing said blocks inside the template.
This mean only the block menu will exists inside child.twig.
In your first example you are actually just overwriting the block menu and creating a new block menu_country inside of it.
In your second example, you are telling twig to output the default content of the block menu and append a new block menu_country to it.
A possible solution would be to change set-up to this e.g.
menu.twig
<header>
<div>
{% block menu_main %}
{% include 'menu-main.twig' %}
{% endblock %}
{% block menu_country %}
{% include 'menu-country.twig' %}
{% endblock %}
</div>
</header>
menu-country.twig
<ul class="country">
{% for country in menu_country|default(dropdownland) %}
<li>{{ country }}</li>
{% endfor %}
</ul>
child.twig
{% extends "base.twig" %}
{% block menu %}
{% include 'menu.twig' with { menu_country: menu_ap_countries, } %}
{% endblock %}
demo

extend or include multiple instances TWIG

I am using Twig to use as templating engine and i have a common right sidebar that shall be included on all pages.
_base.twig
/* Page HTML header */
{% block content %} {% endblock %}
/* Page HTML Footer */
index.twig
{% extends "_base.twig" %}
{% block content %}
<div id="left-bar">Dynamic Content</div>
<div id="right-bar">Static Content</div>
{% endblock %}
about.twig
{% block content %}
<div id="left-bar">Dynamic Content</div>
<div id="right-bar">Static Content</div>
{% endblock %}
.... so on for many pages.. I want right-bar to be a external page and shall be included every page as content is same always.
I tried using {% extends %} two times but Twig fails as i read that multiple extends are not allowed. So any solutions please.
With Twig, you have many possibilities for solving your problem :
A/ The parent solution Official doc is here
This solution renders the contents of the parent block in your child Block.
_base.twig
/* Page HTML header */
{% block content %}
<div id="left-bar">Dynamic Content</div>
<div id="right-bar">Static Content</div>
{% endblock %}
/* Page HTML Footer */
index.twig
{% extends "_base.twig" %}
{% block content %}
{{ parent() }}
{% endblock %}
about.twig
{% extends "_base.twig" %}
{% block content %}
{{ parent() }}
{% endblock %}
other.twig
{% extends "_base.twig" %}
{% block content %}
<div> If you don't place the parent twig attribute, your menu can't appear and will be overrided</div>
{% endblock %}
B/ The Include Solution Official doc is here
This solution create another block, and call the block in the child block.
_base.twig
/* Page HTML header */
{% block content %}
{% endblock %}
/* Page HTML Footer */
navBar.twig
<div id="left-bar">Dynamic Content</div>
<div id="right-bar">Static Content</div>
index.twig
{% extends "_base.twig" %}
{% block content %}
{{ include('navBar.twig') }}\]
{% endblock %}
about.twig
{% extends "_base.twig" %}
{% block content %}
{{ include('navBar.twig') }}
{% endblock %}
If you use the Symfony2 Framework, you can already use the {{ render(controller('')) }} [you can see a sample here] for call a specific controller, himself call a twig view... it's very useful if you want to load a dynamic values in your block, but is less effective than a {{ include('') }}...
For me, the B solution is the best, but both solutions work.

Symfony2: Twig blocks doesn't show up

I'm learning symfony2 and I came across this problem.
I have index.html.twig in my "ShopBundle", which look like this:
{% extends '::base.html.twig' %}
{% block body %}
<div class="container">
<h2>Index template</h2>
<div class="col-md-6">
{% block login %}{% endblock %}
</div>
<div class="col-md-6">
{% block profile%}{% endblock %}
</div>
</div>
{% endblock %}
as you can see I want to display login form and some user info in my index.
Here is my login form twig
{% extends "FOSUserBundle::layout.html.twig" %}
{% trans_default_domain 'FOSUserBundle' %}
{% block login %}
{% block fos_user_content %}
.....
{% endblock fos_user_content %}
{% endblock login %}
and here is my profile info twig
{% trans_default_domain 'FOSUserBundle' %}
{% block profile %}
{% block fos_user_content %}
...
{% endblock fos_user_content %}
{% endblock profile %}
And here is my file structure
So why it's not working? What am I doing wrong? Is it even possible to do it like this?
Thank you very much for all advices.

How to check if a block is overridden in Twig?

I would like to check whether a block is overriden in a child template.
template.html.twig:
<html>
...
<nav class="menu">
{% block menu %}
{% endblock %}
</nav>
...
<div class="contents">
{% block contents %}
{% endblock %}
</div>
...
</html>
page1.html.twig -- contains a menu:
{% extends '::template.html.twig' %}
{% block menu %}
<ul>
<li>...</li>
</ul>
{% endblock %}
{% block contents %}
Hello World!
{% endblock %}
page2.html.twig -- does not contain a menu:
{% extends '::template.html.twig' %}
{% block contents %}
Hello World!
{% endblock %}
I would like to show the nav element in the template, only if it is overriden in the child template (the goal is to avoid an empty <nav /> element).
The Twig syntax has a is empty test, but it only applies to variables, not blocks.
What am I doing wrong?
You can set a variable in your child template check for that:
{# template.html.twig #}
{% if show_menu is not empty %}
<nav class="menu">
{% block menu %}
{% endblock %}
</nav>
{% endif %}
{# page1.html.twig #}
{% set show_menu = true %}
Maybe you should also consider putting this markup in the block definition for a more straight-forward approach.
You could add a wrapper to your nav element and then override it in page2 to make it blank when you don't want a menu. This doesn't require an extra variable.
template.html.twig
... snip ...
{% block nav %}
<nav class="menu">
{% block menu %}
{% endblock %}
</nav>
{% endblock %}
... snip ...
page2.html.twig
{% extends '::template.html.twig' %}
{% block nav '' %} {# here's the kicker ... #}
{% block contents %}
Hello World!
{% endblock %}
In any other page that has a menu you would continue to override the 'menu' block normally.

Categories