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 %}
Related
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?
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
I'm new to symfony2 and I'm looking for solution to create an event that render a twig file.
Assume we have a base template
<!DOCTYPE html>
<html>
<head>
{% block meta %}{% endblock %}
<title>{% block title %}Welcome!{% endblock %}</title>
{% block stylesheets %}{% endblock %}
</head>
<body data-cachetime="{{ cacheTimeStamp() }}">
{% block header %}{% endblock %}
<div id="content">
<div class="container">
{% block content %}{% endblock %}
</div>
</div>
{% block footer %}{% endblock %}
{% block version %}{% endblock %}
{% block javascripts %}{% endblock %}
{% block trackers %}{% endblock %}
</body>
</html>
As you can see, I've a "version" block. I've created an VersionBundle which read the version from a file. The version twig template
{% block version %}
<div id="version"><p>Version: {{ versionString() }}</p></div>
{% endblock %}
calls an "ViewHelper" (i came from Zend ;-)) which calls the function from VersionBundle. But now the tricky part:
The VerionBundle is only registered for 'dev' and 'test' Environment in the AppKernel.
Thats why i create the 'version' block instead of calling the ViewHelper directly in the base twig file.
But i don't know how to create an event to render the version twig template first so the data will passed to the base twig.
I would maybe change the way you try to do it, using more Symfony features and avoiding defining Zend like "helpers". It's a good rule of thumb to keep Twig as minimal as it can be, just rendering view from data the templates receive.
First, define the version variable in parameters_dev.yml and parameters_test.yml files (in app/config). Example:
parameters:
version: 32
Define it as version: ~ in parameters.yml.
Make this variable available in all Twig templates in the app/config/config.yml file:
twig:
globals:
version: %version%
Then, in your version block definition, do the following:
{% block version %}
{% if version is not null %}
<div id="version"><p>Version: {{ version }}</p></div>
{% endif %}
{% endblock %}
Hope it helps!
I have an app I have been building in symfony and I am trying to only display content when the user is on a certain page. I have a if/else block (shown below) but the content still displays on the page I do not what it too. Am I doing this the wrong way?
the if else block
{% block nav %}
{% set uri = app.request.uri %}
{% if uri != 'login' %}
{% include "StarnesUserBundle::user-nav.html.twig" %}
{% endif %}
{% endblock %}
so it is supposed to display the user-nav.html.twig on every page that is not the /login page.
Define a variable in the Twig template of the login page:
{% set hide_user_nav = true %}
Add a test for this variable in your layout:
{% if (hide_user_nav is not defined) %}
{% block nav %}
{% include "StarnesUserBundle::user-nav.html.twig" %}
{% endblock %}
{% endif %}
Something like this should do the trick. this is untested, but should give you a general idea.
{% block nav %}
{% if app.request.attributes.get('_route') != 'login' %}
{% include "StarnesUserBundle::user-nav.html.twig" %}
{% endif %}
{% endblock %}
This assumes your /login route name is login, so just replace login with the name of your route
If the login page extends the layout page that contains this block then you could just put an empty
{% block nav %}{% endblock nav %}
that would override the parent nav block.
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.