Recursive macro call in twig template - twig

After I upgraded my Silex installation to Version 2.x, the macros that I wrote previously do not work.
{% macro render_map(data) %}
/**
* Do some stuff
*/
{{ __self.render_map(argument) }}
{% endmacro %}
After some research I find that the "__self" operator was deprecated and I tried this solution:
{% import _self as macros %}
{% macro render_map(data) %}
/**
* Do some stuff
*/
{{ macros.render_map(argument) }}
{% endmacro %}
Has anyone a solution for my issue?

From the docs:
When you want to use a macro in another macro from the same file, you
need to import it locally: ...
So:
{% macro render_map(data) %}
{% import _self as macros %}
// ...
{{ macros.render_map(argument) }}
{% endmacro %}
Demo: https://twigfiddle.com/jtfo4h

Related

Loop nested array in twig

Im trying to loop through this (for me) quite complex array with Twig. I want to be able to print out all elements. How would you do that? Below is the array and the code im currently trying.
This is the array: https://pastebin.com/TZSANFpW
I have tried this so far but it gives me error: "Notice: Array to string conversion in "
{% for route in routes %}
<p>
{{ route.admin }}
</p>
{% endfor %}
In order to read out a full array, you would to create some form of recursion.
You could achieve this with a macro in the line of
macros.twig
{% macro readArray(array) %}
{% import _self as macros %}
{% if not array is iterable %}
{{ array }}
{% else %}
{% for k,v in array %}
<ul>
<li>
{{ k }}: {{ macros.readArray(v) }}
</li>
</ul>
{% endfor %}
{% endif %}
{% endmacro %}
main.twig
{% import "macros.twig" as macros %}
{{ macros.readArray(results) }}
example

Use grandparent block

I have the following twig templates:
{# layout.twig #}
{% block content %}
THIS IS LAYOUT
{% endblock %}
{# secondary_layout.twig #}
{% extends layout.twig %}
{% block content %}
THIS IS SECONDARY_LAYOUT
{% endblock %}
{# mypage.twig #}
{% extends secondary_layout.twig %}
{% block content %}
{# I WOULD LIKE TO USE layout content block here #}
{% endblock %}
I can call parent() inside the content block in mypage.twig, but how to use a grandparent instead?
There are situations, in which you can achieve this. Unfortunately not in your case. However, it works if only "horizontal re-use" is used (use keyword), but not with inheritance (extends). This for instance applies to form themes.
In my case I defined a form theme which inherits from the bootstrap 3 form theme. The bootstrap theme itself inherits from "form_div_layout". I wanted to override the choice widget and include the grand parent's (form_div_layout) block content, because the bootstrap version of the block didn't fit for me in this single case. So, basically a very similar problem.
This can be solved by inheriting from both, the parent (bootstrap_3_layout) and grand parent layout (form_div_layout), while declaring an alias for the grand parent block to be overridden:
{# my_form_theme.html.twig #}
{% use 'form_div_layout.html.twig' with choice_widget_collapsed as base_choice_widget_collapsed %}
{% use 'bootstrap_3_layout.html.twig' %}
{% block choice_widget_collapsed -%}
{# There is no "grandparent()" function, so instead we can do this: #}
{{- block('base_choice_widget_collapsed') -}}
{%- endblock %}
I'm writing this answer, although it doesn't answer the actual question. But other people will probably also find this question when googling for such a "grandparent"-feature and maybe they'll give up unnecessarily, when they read that it's impossible here.
Ok by writing the problem I got one solution, just modify secondary_layout
{# secondary_layout.twig #}
{% extends layout.twig %}
{% block content %}
{% if use_layout_block %}
{{ parent() }}
{% else %}
THIS IS SECONDARY_LAYOUT
{% endif %}
{% endblock %}
{# mypage.twig #}
{% extends secondary_layout.twig %}
{% block content %}
{% set use_layout_block = true %}
{% endblock %}
It may help someone.
If someone got another solution, feel free to answer.
You can include the block of any template with the function block().
block - Documentation - Twig
block(<block_name>, [template_name])
In your case, the solution could be as follows:
{# mypage.twig #}
{% extends secondary_layout.twig %}
{% block content %}
{{ block('content', 'layout.twig') }}
{% endblock %}

Accessing associative array in twig

I have this array:
$modules = array(
'users',
'submodule' => array(
'submodule1',
'submodule2',
),
);
My question is how can I access all the values and display it on html?
I have tried the following but no luck :
{% for key, module in modules %}
{% if modules.key is iterable %}
{{ module }}
{$ else %}
{{ module }}
{% endif %}
{% endfor %}
Thanks!
If your array has only 2 levels, you can just do something close to what you did:
{% for module in modules %}
{% if module is iterable %}
{% for submodule in module %}
<p>{{ submodule }}</p>
{% endfor %}
{% else %}
<p>{{ module }}</p>
{% endif %}
{% endfor %}
Will give you (with the context you given):
<p>users</p>
<p>submodule1</p>
<p>submodule2</p>
See fiddle
But if your array has an arbitrary number of levels, you should do some recursive using macros:
{% macro show_array(array) %}
{% from _self import show_array %}
{% for module in array %}
{% if module is iterable %}
{{ show_array(module) }}
{% else %}
<p>{{ module }}</p>
{% endif %}
{% endfor %}
{% endmacro %}
{% from _self import show_array %}
{{ show_array(modules) }}
With the following context (in YAML format):
modules:
0: users
submodule:
0: submodule1
1: submodule2
subsubmodule:
0: subsubmodule1
1: subsubmodule2
This will give you:
<p>users</p>
<p>submodule1</p>
<p>submodule2</p>
<p>subsubmodule1</p>
<p>subsubmodule2</p>
See fiddle

How to reuse block in an included template using twig

I have a userDashboard.html.twig template like this:
{% extends "AcmeDemoBundle::base.html.twig" %}
{% block content %}
<h1>Name</h1>
{% endblock %}
{% include "AcmeDemoBundle::statistics.html.twig" %}
The controller call this template(userDashboard).
And a statistics.html.twig where I try to override or extend the content block:
{% extends "AcmeDemoBundle::userDashboard.html.twig" %}
{% block content %}
{{ parent() }}
Something
{% endblock %}
My problem is that I can't do this way. Can somebody recommend a solution?
There's embed which basically lets you include templates while overriding some of their blocks:
{% embed "AcmeDemoBundle::userDashboard.html.twig" %}
{% block content %}
{{ parent() }}
Something
{% endblock %}
{% endembed %}

Dynamic block name in TWIG

I need to add multiple blocks in my template, every with different name.
{% for item from items %}
{% block item.name %}sometext{% endblock %}
{% endfor %}
But I get error. How can I do this ?
In
Dynamic block names are not possible with Twig. There has been a discussion about it over at GitHub.
You can load blocks dynamically using the block function.
{% for item in items %}
{{ block( item.name )|raw }}
{% endfor %}
Twig documentation for the block function
If I understood the question correctly, you can do this (use parent context):
parent.html.twig
{% for item from items %}
{% set currentLoopItemName = item.name %}
{% block item_loop %}sometext{% endblock %}
{% endfor %}
override.html.twig
{% extends "base.html" %}
{% block item_loop %}
{% if item.name == 'custom' %}
// do something
{% else %}
{{ parent() }}
{% endif %}
{% endblock %}
I was attempting to do the same thing and found a solution using the template_from_string function.
_items.html.twig
{% for item in items %}
{{ '{% block ' ~ item.name ~ ' %}'}}
sometext
{{ '{% endblock %}' }}
{% endfor %}
enter code here
page.html.twig
{% embed template_from_string(include('_items.html.twig')) %}
{% block someItemName %} someDifferentText {% endblock %}
{% endembed %}
What's happening is the block tags are initially being created as text. Then we use the include function to get the rendered content of _items, as a string. Finally, we convert that string to a working template (which we can embed or extend).
This works because the template_from_string function will create and compile a template at runtime, where as normally twig is compiled before hand and unchanged at runtime.

Resources