How can I reuse a block multiple times? - symfony

How can I render a block multiple times within a view in a twig template? Currently I do have a block, let's call it something defined in an included (via render) twig file. I then include it at the layout-twig file using the following:
{% block something %}{% endblock %}
Everything is fine, however as soon as I call this tag a second time, I get the following error message:
The block 'something ' has already been defined in "::layout.html.twig"
Therefore: How can I render a block multiple times?

The notation you're showing us is for defining and rendering a block. Rendering only is done this way:
{{ block('blockName') }}

Related

Variable entity doesn't exist in template, how to fix it?

Here is my template architecture :
list.html.twig => question.html.twig => tagList.html.twig => tagBadge.html.twig
In my tag list, I have a tag variable that is well recognized, but in the tagBadge that is included, I get the error Variable tag does not exist.
I don't know what is the origin of the error. Maybe Twig can't follow the track of variables after a certain depth of inclusion ? I checked for invisible characters in my files. I verified my syntax again and again, but maybe my error is simple...
tagList.html.twig
{% for tag in tags %}
{{tag.name}}{# tag is recognized #}
{% include 'tag/partials/tagBadge.html.twig' with {'tag': tag} %}
{# tag is not recognized in template #}
{% endfor %}
tagBadge.html.twig
{{tag.name}}
My question: why tag is not recognized in my second template 'tagBadge'. I'm working with Symfony 4.2.
Ok, I found this week end the mistake I did. It appeared that I used my template in to different places of my view. It wasn't that use of the template which throw the error, but another I totally forgot, to which I didn't pass the variable... I feel dumb ;) let my mistake be useful for others !

Block header in symfony, twig

I'm wondering...
If I have a website where I use two headers
header.homepage.html.twig - only used at main page
header.other.html.twig - used everywhere else
Which one should I put into
{% block header %} {% include 'main/header.?.html.twig' %} {% endblock header %}
in base.html.twig?
I'm almost sure that I can do whatever I want - in this case put header.other.html.twig there, as it is used more times. So I need only include header.homepage.html.twig once, in index. BUT I always see people around putting the main header of the page in base.
So my question is - Including the main header in base.html.twig is the best practice or some people do it out of habit?

Silex - How to process template includes separately

I've got a Twig base template like so:
{% include "./partials/navigation.html" %}
<!-- Main Wrapper -->
<div id="wrapper">
{% block content %}{% endblock content %}
</div>
I also have a route controller which is outputting the response content to the page using twig:
return template->render('path/to/teplate', args());
where args[] array is all the data needed for this bit: (different on every page)
{% block content %}{% endblock content %}
However my sidebar is being built separately through a menu builder class, and now it needs to render the results of building the menu to my template and populating ./partials/navigation.html.
An easy solution for me is to just append the results of the Menu Builder to the returned response of every controller (I can use a base controller class to do this as this menu appears on every page). However something about this feels unclean as if I have to render the same header/footer every time as well I'll have to be append all 3 outputs to the response. (maybe that is okay?)
Is there a better way of rendering several includes worth of content which each need their own DB lookups and other server-side logic?
Yes. They are called sub requests. Read the documentation here.

Twig: render Symfony2 controllers which extend blocks in the parent template

Say I'm having a base template like this:
// Default/index.html.twig
{% block javascripts %}
<script>//some script</script>
{% endblock %}
<div>
{{ render(controller(MyControllerBundle:Default:header)) }}
</div>
{{ text }}
<div>
{{ render(controller(MyControllerBundle:Default:footer)) }}
</div>
And this renders controllers having these templates:
// Default/header.html.twig
Header content
{% block javascripts %}
<script>//some additional scripts from the header</script>
{% endblock %}
and
// Default/footer.html.twig
Footer content
{% block javascripts %}
<script>//some additional scripts from the footer</script>
{% endblock %}
Is it possible somehow to use the javascripts block from the rendered sub controllers in the parent template?
I want to have all javascripts cumulated in one place.
Rendering from bottom up with extending is no option here because the template consists of
multiple blocks that are rendered by different controllers.
Is this possible somehow? Or is there a better approach to this?
Anything is possible however design-wise it might not be a good idea.
The render tag is really useful when it comes down to scaling and it used as a way to isolate a request. Each render call is considered as a sub-request and a cache strategy can be applied to it.
I'd highly advise you to read this documentation about HTTP caching and especially the part that talks about Edge Side Includes (or ESI).
When you use the render tag, think of it as a module you want to include in multiple pages and eventually cache.
You shouldn't interact with the master request because the sub request is isolated for caching (depending on the place you embed the render tag, the master request will be different which means you might get some unexpected results).
First of all, I'd create a layout template that every other pages extends. The layout template will declare all the basic blocks (javascript, css, footer, header, <head>, <body> - you can abstract in more templates if you want).
If you have logic for your footer or header split them into Twig functions (or filters) and handle the logic in Twig but keep it light (if it's too complicated or too spaghetti that means there is another way).
Avoid having multiple Javascript or CSS files per page. If you have some css or javascript that appears on some pages but not all of them it's still probably a good idea to merge them into one file (less DNS calls on the client side and once it's cached it will be faster to load the page).
If you have a administrator.js kind-of file, then you could include it as a separate file but if most requests come from administrators then you might want to include it with all the other files.
If you didn't know you can combine assets (js or css) into one file: more info in the Symfony documentation.
I didn't answer your "how" question because I'd strongly advise you to not implement such a system however I believe I've shared good guidelines to make an informed decision.
when extending / rendering other content in TWIG you can call the parent block: http://twig.sensiolabs.org/doc/functions/parent.html
this means that you can leave default as it is and inside header / footer define
{% block javascripts %}
{{ parent() }}
{# other scripts #}
{% endblock javascripts %}
I would suggest that you have different block name for the footer - that way you can include scripts outside of the header.
Also, it might be best to keeps scripts in one place - that way you can use assetic rewrite's later down the line : http://symfony.com/doc/current/cookbook/assetic/asset_management.html#including-javascript-files
exactly what #Pazi says in the comment: Do you need a controller? It looks pretty simple to just include the template by itself, without using a controller.
You might use the include tag to include the subtemplates.
{% include 'MyControllerBundle:Default:header.html.twig' %}
For reusing the javascript block from the rendered sub controllers, you could create a base template that contains the javascripts block. Then extend that base template file in your header and footer. Or just including the base template in them should work, too.

How to reference twig for custom field types in dedicated bundle?

I am (still) trying to introduce http://xoxco.com/clickable/jquery-tags-input into a dedicated bundle. As far, I have a type as a child of text and a data transformer that converts comma-separated strings into arrays of Objects and vice versa.
Now I want to decorate the text field with the JQuery code linked above. As far as I understand, I have to define a block like
{% block manytomanycomboselector_widget %}
{% spaceless %}
{{ block('text_widget') }}
<script>
$(function(){
$("#{{ id }}").tagsInput();
});
</script>
{% endspaceless %}
{% endblock manytomanycomboselector_widget %}
in [MyTypeBundle]Resources/views/Form/fields.html.twig
Now, both the documentation and the answers for this question at StackOverflow state that I have to reference fields.html.twig somewhere either in the template that uses the form or in app/, but this doesn't seem to be necessarily for other field-type bundles, though I cannot see in their code why.
What do I have to configure inside the bundle besides this block in this file?
Also I didn't get where I have to put the css and js requirements for the header and how I deal with general requirements like jQuery itself.
I have the same issue & I resolve it by merging my field template in the twig.form.resources parameter.
So, in the DI extension of my bundle (MyBundle/DependencyInjection/MyBundleExtension.php), I add:
$container->setParameter('twig.form.resources', array_merge(
array('MyBundle:Form:field_widget.html.twig'),
$container->getParameter('twig.form.resources')
));
Be aware, your bundle must be registered after the TwigBundle in your AppKernel.
EDIT:
A form field is not linked to any JS or CSS. So, IMO, you have 2 solutions.
Firstly, you directly wrap your JS & CSS in your field template and your bundle stays stand-alone.
Secondly, you instruct final users that they need to include manually some JSS & CSS each time they use your field type.
The IoFormBundle & GenemuFormBundle uses the second solution like explain in their documentation.

Resources