Twig templates, inheritances and block usage - php

I've created three templates using Twig. The first one has block A defined in it, the second one extends from the first one, but includes a third template which sets the content of block A.
When loading, through the browser, the url which renders b.html.twig, the content in block A (defined by the 3th template) is not positioned block _A is defined.
Example:
<!-- base.html.twig -->
{% block _css '' %}
{% block _header '' %}
{% block _content '' %}
{% block _footer '' %}
{% block _js '' %}
<!-- layout.html.twig -->
<!-- header and footer are placed in the raight zone -->
{% extends ::base.html.twig %}
{% block _header %}
{% render "MyBundleBundle:Header:header" %}
{% endblock %}
{% block _footer %}
{% render "MyBundleBundle:Footer:footer" %}
{% endblock %}
<!-- my_template.html.twig -->
<!-- content is also placed in the right zone but css and js blocks in the included template are not placed where declared in base.html.twig -->
{% extends MyBundleBundle::layout.html.twig %}
{% block _content %}
SOME_CONTENT
{% include MyBundleBundle::my_included_template.html.twig %}
{% endblock %}
<!-- my_included_template.html.twig -->
{% block _css %}
<link.......................>
{% endblock %}
{% block _js %}
<script..................>
{% endblock %}
MORE CONTENT BELONGING TO THE INCLUDED TEMPLATE
What i expect here is, _css blocks content to appear on top of the page and _js block content at the bottom, but that's not happening. I hope you can see where i'm going wrong, thanks!

The include tag does not work the way you think/hope it does. It simply includes the file into the current context. Which means you can't reference blocks. You can only reference blocks in a template that has the extends tag at the top of the file.
You're going to have to restructure the way you've designed your template to make this work for you. Instead of including my_included_template.html.twig you should possibly just render that template directly in your controller. That way it'll have full access to any inherited blocks.

Related

Two templates showed

I'm using FOSUserBundle on my Symfony4 project, and I overrode all bundle templates, and place them on my templates folder.
I have my base.html.twig file, is the main page template that contains initial HTML script for all stylesheets and javascript files. On the body I added the content block:
<body>
{% block content %}{% endblock content %}
</body>
Then, on my login template, I extended the main template and added the fos_user_content block:
{% extends 'bundles/landing.html.twig' %}
{% block content %}
{% block fos_user_content %}
{% endblock content %}
And for the moment everything is ok. The login form is displayed well. But when I log into this form, the system switched me to the Profile page. The problem, is that, the profile page template is showed and the login template too.
Both the templates are used.
On the profile template, I extended the main template too + added the same block content, except the fos_user_content block.
You missed an {% endblock fos_user_content %} tag
{% extends 'bundles/landing.html.twig' %}
{% block content %}
{% block fos_user_content %}{% endblock fos_user_content %}
{% endblock content %}

render option twice in base.html.twig

I got base.html.twig file inside i wanna render two controller
Is that is possible ?
</head>
<body>
{% block header %}{{ render(controller('App\\Controller\\FrontController::front_header')) }}{% endblock %}
{% block content %}{% endblock %}
{% block footer %}{{ render(controller('App\\Controller\\FrontController::front_footer')) }}{% endblock %}
</body>
{% block javascripts %}{% endblock %}
or what way is the best to handle something like that ?
becouse frontcontroller render anothe index.html.twig which will get some stuff to block content and i wanna have this block content between two another controllers.
why not simply use Twig include for your header and your footer?

Difference between Include, Extends, Use, Macro, Embed in Twig

What is the difference between use and include in Twig?
Documentation:
include
The include statement includes a template and returns the rendered content of that template into the current one:
{% include 'header.html' %}
Body here...
{% include 'footer.html' %}
use
The use statement tells Twig to import the blocks defined in blocks.html into the current template (it's like macros, but for blocks):
blocks.html
{% block sidebar %}{% endblock %}
main.html
{% extends "base.html" %}
{% use "blocks.html" %}
{% block title %}{% endblock %}
{% block content %}{% endblock %}
Possible answer:
I think this should explain the difference:
include is to get all the code from an external file and import it
into your actual file at the right location of the call.
use is completely different as it parses the linked file to find a
particular section of code and then overwrites the blocks with the
same name, in your current file, with the one found in this external
file.
include is like "go find this file and render it with my page here".
use is "parse this other file to find block definitions to use instead
of my owns defined here".
If use command finds nothing matching the task, nothing is displayed
at all from this file.
Question
is the explanation correct? are there any other explanations to this difference?
After months, I am posting an answer for any further reference to this question. I also added some description for extends & import & macro & embed for more clearance:
There are various types of inheritance and code reuse in Twig:
Include
Main Goal: Code Reuse
Use Case: Using header.html.twig & footer.html.twig inside base.html.twig.
header.html.twig
<nav>
<div>Homepage</div>
<div>About</div>
</nav>
footer.html.twig
<footer>
<div>Copyright</div>
</footer>
base.html.twig
{% include 'header.html.twig' %}
<main>{% block main %}{% endblock %}</main>
{% include 'footer.html.twig' %}
Extends
Main Goal: Vertical Reuse
Use Case: Extending base.html.twig inside homepage.html.twig & about.html.twig.
base.html.twig
{% include 'header.html.twig' %}
<main>{% block main %}{% endblock %}</main>
{% include 'footer.html.twig' %}
homepage.html.twig
{% extends 'base.html.twig' %}
{% block main %}
<p>Homepage</p>
{% endblock %}
about.html.twig
{% extends 'base.html.twig' %}
{% block main %}
<p>About page</p>
{% endblock %}
Use
Main Goal: Horizontal Reuse
Use Case: sidebar.html.twig in single.product.html.twig & single.service.html.twig.
sidebar.html.twig
{% block sidebar %}<aside>This is sidebar</aside>{% endblock %}
single.product.html.twig
{% extends 'product.layout.html.twig' %}
{% use 'sidebar.html.twig' %}
{% block main %}<main>Product page</main>{% endblock %}
single.service.html.twig
{% extends 'service.layout.html.twig' %}
{% use 'sidebar.html.twig' %}
{% block main %}<main>Service page</main>{% endblock %}
Notes:
It's like macros, but for blocks.
The use tag only imports a template if it does not extend another template, if it does not define macros, and if the body is empty.
Macro
Main Goal: Reusable Markup with Variables
Use Case: A function which gets some variables and outputs some markup.
form.html.twig
{% macro input(name, value, type) %}
<input type="{{ type|default('text') }}" name="{{ name }}" value="{{ value|e }}" }}" />
{% endmacro %}
profile.service.html.twig
{% import "form.html.twig" as form %}
<form action="/login" method="post">
<div>{{ form.input('username') }}</div>
<div>{{ form.input('password') }}</div>
<div>{{ form.input('submit', 'Submit', 'submit') }}</div>
</form>
Embed
Main Goal: Block Overriding
Use Case: Embedding pagination.html.twig in product.table.html.twig & service.table.html.twig.
pagination.html.twig
<div id="pagination">
<div>{% block first %}{% endblock %}</div>
{% for i in (min + 1)..(max - 1) %}
<div>{{ i }}</div>
{% endfor %}
<div>{% block last %}{% endblock %}</div>
</div>
product.table.html.twig
{% set min, max = 1, products.itemPerPage %}
{% embed 'pagination.html.twig' %}
{% block first %}First Product Page{% endblock %}
{% block last %}Last Product Page{% endblock %}
{% endembed %}
service.table.html.twig
{% set min, max = 1, services.itemPerPage %}
{% embed 'pagination.html.twig' %}
{% block first %}First Service Page{% endblock %}
{% block last %}Last Service Page{% endblock %}
{% endembed %}
Please note that embedded file (pagination.html.twig) has access to the current context (min, max variables).
Also you may pass extra variables to the embedded file:
pagination.html.twig
<p>{{ count }} items</p>
<div>
<div>{% block first %}{% endblock %}</div>
{% for i in (min + 1)..(max - 1) %}
<div>{{ i }}</div>
{% endfor %}
<div>{% block last %}{% endblock %}</div>
</div>
product.table.html.twig
{% set min, max = 1, products|length %}
{% embed 'pagination.html.twig' with {'count': products|length } %}
{% block first %}First Product Page{% endblock %}
{% block last %}Last Product Page{% endblock %}
{% endembed %}
Note:
It has functionality of both Use & Include together.
Twig performance is spectacular, so chances are you will never care, but be aware that there is a significant difference between using embeds, includes, or macros.
I set up a little test project to demonstrate it: https://github.com/janklan/twig-benchmark
The readme has all the information you need if you're interested to know more, but the general result is, that for the same task executed in a loop of 100k iterations, the times between individual methods on my computer were as follows (lower is better)
embed: 4253ms
include: 3428ms
macro: 2695ms
By the way, the project I shared via Github up generates separate cache directories for each approach (embed/include/macro). If you want to see how different methods in Twig map to the compiled PHP, I recommend you check it out. If you wish to see the cache without having to run the project, check out a branch called with-cache

Twig: Override elements with embed

I have the following template structure and I want to override a block with embed:
base.html.twig
<!DOCTYPE>
<html>
<head></head>
<body>
{% embed 'header.html.twig' %}
{% block content %}{% endblock %}
{% endembed %}
</body>
</html>
header.html.twig
{% block content %}{% endblock %}
page.html.twig
{% extends 'base.html.twig' %}
{% block content %}
<p>Some content</p>
{% endblock %}
If I load the page.html.twig with my controller now I thought it should display "Some content" but it doesn't work. Does anyone have an idea what I did wrong?
"The embed tag combines the behaviour of include and extends. It allows you to include another template's contents, just like include does. But it also allows you to override any block defined inside the included template, like when extending a template."
twig document about embed block
You can use import or include. I recommend include fot this state

twig: appending to parent template region from multiple includes

general.tpl:
<head>
<!-- some default meta tags -->
</head>
<body>
{% block mainArea %}{% endblock %}
{% block sidebar %}{% endblock %}
<!-- standard footer -->
<script src="base.js"></script>
{% block js %}{% endblock %}
</body>
page1.tpl:
{% extends "base.tpl" %}
{% block mainArea %}
some content
{% endblock %}
{% block sidebar %}
...
{% include 'one-of-many-widgets.tpl' %}
...
{% endblock %}
one-of-many-widgets.tpl
requires specific javascript library that must be referenced before <body> tag and needs to emit specific in-line javascript before that library but after referencing base.js
requres specific meta tags to be added to page head block
requires to append additional legal information to page footer
Q1: How to do that by putting additional script and meta info into one-of-many-widgets.tpl file? Given that there is several included widgets that all contribute to scripts area of page.
Q2: there is also page2.tpl, that also need it's own additional js and extended meta tags, in addition to emitted by includes. Is it possible to combine them but not overwrite?
one-of-many-widgets-1.tpl:
{% block jsPreBodyAdditions %}
{{ alreadyCollected() }} widget script tag or inline js to APPEND
{% endblock %}
{% block metaTags %}
{{ alreadyCollected() }} widget additional semantic meta tags to APPEND
{% endblock %}
one-of-many-widgets-2.tpl:
{% block jsPreBodyAdditions %}
{{ alreadyCollected() }} widget script tag or inline js to APPEND
{% endblock %}
{% block metaTags %}
{{ alreadyCollected() }} widget additional semantic meta tags to APPEND
{% endblock %}
page2.tpl:
{% block jsPreBodyAdditions %}
{{ alreadyCollected() }} page2 script tag or inline js to APPEND
{% endblock %}
{% block metaTags %}
{{ alreadyCollected() }} page2 additional semantic meta tags to APPEND
{% endblock %}

Categories