I using twig to render data, to html. Data I got from server is a key-value. for example :
| Key | Value |
|-----|-------|
| A | 1 |
| A | 2 |
| B | |
| C | 10 |
I need to render this data to table like this :
| A | B | C |
|---|---|----|
| 1 | 0 | 10 |
| 2 | 0 | 0 |
this is what i've tried :
<table>
<thead>
<tr>
{% set now = '' %}
{% set arrKey = [] %}
{% for data in datas %}
{% if now != data.key %}
<th>{{ data.key }}</th>
{% set now = data.key %}
{% else %}
{% set arrKey = arrKey|merge([now]) %}
{% endif %}
{% endfor %}
</tr>
</thead>
<tbody>
{% set rowNow = '' %}
{% for key in arrKey %}
<tr>
{% for data in datas %}
{% if rowNow != dataKey %}
{% if data.value is empty %}
<td>0</td>
{% else %}
<td>{{ data.value }}</td>
{% endif %}
{% set rowNow = data.key %}
{% endif %}
{% endfor %}
</tr>
{% endfor %}
</tbody>
</table>
but this still wrong, it will render :
| A | B | C |
|---|---|----|
| 1 | 0 | 10 |
| 1 | 0 | 10 |
please help me how to solve this?
The best way to manage this is to convert the data inside the controller. This is because the merge filter is a pain to work with. Anyway if you wanted to do it in pure twig, here is how
{% set data = [
{ 'key': 'A', 'value': 1, },
{ 'key': 'A', 'value': 2, },
{ 'key': 'B', 'value': null, },
{ 'key': 'C', 'value': 2, },
{ 'key': 'C', 'value': 4, },
{ 'key': 'A', 'value': 3, },
{ 'key': 'A', 'value': 4, },
] %}
{% set temp = [] %}
{% for row in data %}
{% if not (row.key in temp|keys) %}
{% set temp = temp | merge({ (row.key) : [] }) %}
{% endif %}
{% set temp = temp | merge({(row.key):(temp[row.key]|merge([ row.value, ]))}) %}
{% endfor %}
{% set max = 0 %}
{% for data in temp %}{% if data|length > max %}{% set max = data|length %}{% endif %}{% endfor %}
<table>
<tr>
{% for key in temp|keys %}
<th>{{ key }}</th>
{% endfor %}
</tr>
{% for i in 0..(max-1) %}
<tr>
{% for data in temp %}
<td>{{ data[i] | default(0) }}</td>
{% endfor %}
</tr>
{% endfor %}
</table>
demo
Related
My goal is having a string like "name: smoothie, ingredients: milk, orange, category: vegan" (there can be a lot option: value or option: value, value, value... pairs) to produce an array like the following
[
{
option: 'name',
value: ['smoothie']
},
{
option: 'ingredients',
value: ['milk', 'orange']
},
{
option: 'category',
value: ['vegan']
}
]
I thought something like the following would work but it produces the following error and i can't understand why.
The problem is in the line where i try to add a value to the existing options[last_ok_index].value array
{% set options[last_ok_index].value = options[last_ok_index].value|merge( [ x[0] ] ) %}
Unexpected token "punctuation" of value "[" ("end of statement block" expected).
{% set product = "name: smoothie, ingredients: milk, orange" %}
{% set options = [] %}
{% set last_ok_index = 0 %}
{% for item in product|split(',') %}
{% set x = item|split(': ') %}
{% if x|length == 2 %}
{% set options = options|merge( [ { option: x[0], value: [x[1]] } ] ) %}
{% set last_ok_index = loop.index - 1 %}
{% else %}
{% set options[last_ok_index].value = options[last_ok_index].value|merge( [ x[0] ] ) %}
{% endif%}
{% endfor %}
{# print result #}
{% for item in options %}
{{item.option }}
{% for inner_item in item.value %}
{{"-" ~ inner_item}}
{% endfor %}
{% endfor %}
You should go with the suggestion in the comments by #dbrumann and use a TwigExtension.
However if you want to solve this in pure twig, then you are overcomplicating things.
First things first, the problem already starts at your first split, your expected output is smoothie and ingredients, while the actual result will be smoothie, ingredients, orange. You can fix this by passiung asecond argument to the split filter, which will limit the output.
Split uses the PHP function explode in the background. More on what the second parameters does you can find in the documentation here
Now as I said you can simply your snippet by creating the "item" in two parts rather than one part
{% set product = "name: smoothie, ingredients: milk, orange" %}
{% set items = [] %}
{% for item in product|split(',', 2) %}
{% set tmp = item|split(':') %}
{% set option = tmp[0] %}
{% set values = [] %}
{% for value in tmp[1]|split(',') %}
{% set values = values|merge([ value, ]) %}
{% endfor %}
{% set items = items|merge([ {'option': option, 'values': values,}, ]) %}
{% endfor %}
demo
As you've changed the initial input of the original question. The problem still starts with the split filter. I'd suggest you use another delimeter for your values, e.g. ;
{% set product = 'name: smoothie 3, ingredients: milk ; orange; pineapple, category: bar' %}
{% set products = [] %}
{% for string in products_raw %}
{% set product = [] %}
{% for item in string|split(',') %}
{% set tmp = item|split(':') %}
{% set option = tmp[0] %}
{% set values = [] %}
{% for value in tmp[1]|split(';') %}
{% set values = values|merge([ value, ]) %}
{% endfor %}
{% set product = product|merge([ {'option': option, 'values': values,}, ]) %}
{% endfor %}
{% set products = products|merge([ product, ]) %}
{% endfor %}
{% for product in products %}
{% for item in product %}
- Option: {{ item.option }}
- Values:
{% for value in item.values %}
- {{value }}
{% endfor %}
{% endfor %}
----------------------------------
{% endfor %}
demo
Thank you a lot for all the tips, i changed the logic a bit and it works now.
I will search about twig extensions as you proposed since it is for sure too much code in twig for something like that.
{% set product = "name: smoothie, ingredients: milk, orange, sugar, tags: healthy, popular, category: milk" %}
{% set options = [] %}
{% set last_option = null %}
{% set last_value = null %}
{% for item in product|split(',') %}
{% set x = item|split(':') %}
{% if x|length == 2 %}
{% if last_value|length > 0 %}
{% set options = options|merge( [ {option: last_option, value: last_value} ] ) %}
{% endif %}
{% set last_option = x[0] %}
{% set last_value = [x[1]] %}
{% else %}
{% set last_value = last_value|merge([x[0]]) %}
{% endif%}
{% if loop.last %}
{% if last_value|length > 0 %}
{% set options = options|merge( [ {option: last_option, value: last_value} ] ) %}
{% endif %}
{% endif %}
{% endfor %}
{# print result #}
{% for item in options %}
{{ item.option }}
{% for inner_item in item.value %}
{{ "-" ~ inner_item }}
{% endfor %}
{% endfor %}
testplugin.testplugin.firstkey has following value 1, 2, 3
I have follwing code written in TWIG:
{% set key1 = [config("testplugin.testplugin.firstkey")] %}
{% for ids in key1 %}
{% set key1 = ids %}
GO-{{ ids }} {% if not loop.last %},{% endif %}
{% endfor %}
The problem is, that the config("testplugin.testplugin.firstkey") won't get parsed correctly. Actually it only gets parsed as one value instead of 3 seperat values in an array. But when i define the values manually - without a variable - it works as it should:
{% set key2 = [1, 2, 3] %}
{% for ids in key2 %}
{% set key2 = ids %}
GO-{{ ids }} {% if not loop.last %},{% endif %}
{% endfor %}
The first code does this:
GO-1, 2, 3
the second one looks like this (as it should):
GO-1, GO-2, GO-3
So my question is, why doeas the first code won't work properly?
I could figure it out by myself:
{% set key1 = config("testplugin.testplugin.firstkey"))|split(',') %}
{% for ids in key1 %}
{% set key1 = ids %}
GO-{{ ids }} {% if not loop.last %},{% endif %}
{% endfor %}
Thanks anyway ;-)
I have a (very big) twig html template, where I display a table and loop through the rows:
{% for assignment in assignments %}
...
{% if assignment.zip.contractor1 is not null %}
{% for priceEK in assignment.zip.contractor1.pricecontractor %}
{% if priceEK.zip == assignment.zip %}
{% set bookDates = date(assignment.start|date).diff(assignment.end).days + 1 %}
{% set priceDay = 0 %}
{% set priceSide = 0 %}
{% if bookDates > 1 %}
{% set priceDay = priceEK.priceDay * (bookDates - 1) %}
{% set priceSide = priceEK.priceAdd * (bookDates) %}
{% endif %}
{% if assignment.meter == 15 %}
{{ (priceEK.price15 + priceDay + priceSide)|number_format(2) }}€
{% elseif assignment.meter == 20 %}
{{ (priceEK.price20 + priceDay + priceSide)|number_format(2) }}€
{% else %}
{{ (priceEK.price25 + priceDay + priceSide)|number_format(2) }}€
{% endif %}
{% endif %}
{% endfor %}
{%endif %}
Now this works like a charm IF, assignment.zip.contractor1 is not null is only true ONE TIME. If it is true a second time while looping through assignments it gives me the following error in my logs:
Error: Nesting level too deep - recursive dependency?
I assume this may be a complicated problem without knowing the database relations, please feel free letting me know what further info is needed (and how to insert here), I'll update accordingly.
//EDIT Maybe there is a way to 'reset' the nested for (priceEK)? {% set priceEK = null %} has no effect though...
//EDIT2: Found sth. else, changed all == to is same as() as described HERE. Now I can get a second loop. If I then have a third one, Firefox crashes and wants to debug the script...
//EDIT: ok, found a solution... Maybe this helps for someone else:
{% for pc in assignment.zip.pricecontractor %}
{% if pc.contractor is same as(assignment.zip.contractor1) %}
{% set bookDates = date(assignment.start|date).diff(assignment.end).days + 1 %}
{% set priceDay = 0 %}
{% set priceSide = 0 %}
{% if bookDates > 1 %}
{% set priceDay = pc.priceDay * (bookDates - 1) %}
{% set priceSide = pc.priceAdd * (bookDates) %}
{% endif %}
{% if assignment.meter is same as(15) %}
{{ (pc.price15 + priceDay + priceSide)|number_format(2) }}€
{% elseif assignment.meter is same as(20) %}
{{ (pc.price20 + priceDay + priceSide)|number_format(2) }}€
{% else %}
{{ (pc.price25 + priceDay + priceSide)|number_format(2) }}€
{% endif %}
{% endif %}
{% endfor %}
How can I represent array data given in the following format:
array(3) {
[0]=> array(4) {["cohort"]=5 ["total"]=5 ["week"]=1 ["active"]=2 }
[1]=> array(4) {["cohort"]=5 ["total"]=5 ["week"]=2 ["active"]=1 }
[2]=> array(4) {["cohort"]=6 ["total"]=3 ["week"]=1 ["active"]=1 }
}
To be shown in twig like this:
cohort| total| 1 (week) | 2 (week)
------------------------------------
5 | 5 | 2 | 1
6 | 3 | 1 | -
My problem, is that I don't know how to loop through the array, so that I only get row numbers equal to unique cohort number (only 5 and 6, not 5,5,6), get 'week' number as column name and 'active' value to be shown in relevant 'week' column and relevant cohort row
I tried to do this:
<table>
<tr><td>Cohort</td><td>Registered users</td>
{% for week in logged_users %}
{% if week.week_number not in weekArray %}
<td>{{week.week_number}}</td>
{% set weekArray = weekArray|merge([week.week_number]) %}
{% endif %}
{% endfor %}
</tr>
{% for data in logged_users %}
<tr>
{% if data.cohort not in cohortArray %}
<td>{{data.cohort}}</td>
<td>{{ data.total_users }}</td>
{% for active in logged_users %}
<td>{{ active.active_users }}</td>
{% endfor %}
{% set cohortArray = cohortArray|merge([data.cohort]) %}
{% endif %}
</tr>
{% endfor %}
</table>
but in the result I get this:
<table>
<thead>
<tr><th>cohort</th><th>total</th><th>1 - week</th><th>2 - week</th></tr>
</thead>
<tbody>
{% for i,a in data %}
<tr>
<td>{{ a.cohort }}</td><td>{{ a.total }}</td><td>{{ (a.week==1 ? a.active : '-') }}</td><td>{{ (a.week==2 ? a.active : '-') }</td>
</tr>
{% endfor %}
</tbody>
</table>
I can't for the life of me figure out how to add a </tr><tr> every OTHER iteration in a Twig loop.
For instance:
$numArray = array(12,13,14,15,16,17,18);
Passed to twig, I would loop a table like:
<table>
{% for num in numArray %}
<tr>
<td>
{{num}}
</td>
</tr>
{% endfor %}
</table>
This would output:
+-----------+
| 12 |
+-----------+
| 13 |
+-----------+
| 14 |
+-----------+
| 15 |
+-----------+
| 16 |
+-----------+
| 17 |
+-----------+
| 18 |
+-----------+
What I'd like to do is get something like this:
+-----------+-----------+
| 12 | 13 |
+-----------+-----------+
| 14 | 15 |
+-----------+-----------+
| 16 | 17 |
+-----------+-----------+
| 18 | |
+-----------+-----------+
But I can't for the life of me figure out a way to alternate my row input with anything that doesn't seem hacky. Honestly I can't even get hacky to work. Is there a method for this? Or, should I be looking to write my own extension?
The proper way of doing this is using the batch filter. It is new in 1.12.3.
<table>
{% for row in numArray|batch(2) %}
<tr>
{% for column in row %}
<td>{{ column }}</td>
{% endfor %}
</tr>
{% endfor %}
</table>
Ref: http://twig.sensiolabs.org/doc/filters/batch.html
Something like this would work:
<table>
<tr>
{% for num in numArray %}
<td>
{{num}}
</td>
{% if loop.index is even %}
</tr>
<tr>
{% endif %}
{% endfor %}
{% if num|length is odd %}
<td></td>
{% endif %}
</tr>
</table>
An alternative way, that feels much less hacky:
<table>
{% for i in range(0, numArray|length-1, 2) %}
<tr>
<td>{{ numArray[i] }}</td>
<td>{{ numArray[i+1]|default("") }}</td>
</tr>
{% endfor %}
</table>
For this specific case you can prepare your array before. So in a loop you will have in each row two variables. Try first example from this site http://twig.sensiolabs.org/doc/templates.html