twig merge error Unexpected token "punctuation" of value "[" - php

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 %}

Related

Key inside Array won't parse correctly in TWIG

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 ;-)

Find value in array and return with new from different array

Currently I have two arrays
{% set code = [AMS, EIN, RTM] %}
{% set city = [Amsterdam, Eindhoven, Rotterdam] %}
I would like to check if the value of {{airport}} exists in the first array and if it is code[0] I would like to change {{airport}} into the value of city[0]. Is this possible with Twig?
You can loop over the code array:
{% for c in code %}
{# ... #}
{% endfor %}
Documentation: https://twig.symfony.com/doc/2.x/tags/for.html
Then if the item does match:
{# ... #}
{% if airport == c %}
{# ... #}
{% endif %}
{# ... #}
Documentation: https://twig.symfony.com/doc/2.x/tags/if.html
Replace the variable airport, at the same loop index:
{# ... #}
{% set airport = city[loop.index0] %}
{# ... #}
Documentation: https://twig.symfony.com/doc/2.x/tags/for.html#the-loop-variable
So, in full:
{% for c in code %}
{% if airport == c %}
{% set airport = city[loop. index0] %}
{% endif %}
{% endfor %}
Running fiddle: https://twigfiddle.com/xflfas/2
Out of the scope note: your arrays would be better named cities and codes.
This way, when you loop over them, you end up with meaningful naming
{% set codes = ['AMS', 'EIN', 'RTM'] %}
{% for code in codes %}
{{ code }}
{% endfor %}
{# and #}
{% set cities = ['Amsterdam', 'Eindhoven', 'Rotterdam'] %}
{% for city in cities %}
{{ city }}
{% endfor %}
Use {% if value in array %} to search from the first array and Twig's merge function to replace the value in the second array. See this https://twig.symfony.com/doc/2.x/filters/merge.html

How to set a custom class to submenus in Drupal 8?

I am working on sidebar menu in a Custom Drupal 8 Theme. I am trying to set a class of sidebar__menu--submenu-1,sidebar__menu--submenu-2, sidebar__menu--submenu-3 and so on depending on the submenu's level.
So far, I was able to add the class sidebar__menu to the first level & sidebar__menu--submenu to all submenu's level. However, I want to add the 'class' sidebar__menu--submenu-(number of the level) so I can style & control the sidebar better with CSS.
Here it is my code menu.html.twig:
{{ menus.menu_links(items, attributes, 0) }}
{% macro menu_links(items, attributes, menu_level, menu_name) %}
{% import _self as menus %}
{%
set menu_classes = [
'sidebar__menu' ~ menu_name|clean_class,
]
%}
{%
set submenu_classes = [
'sidebar__menu' ~ menu_name|clean_class ~ '--submenu',
]
%}
{% if items %}
{% if menu_level == 0 %}
<ul{{ attributes.addClass('container mx-auto', menu_classes) }}>
{% else %}
<ul {{ attributes.removeClass(menu_classes).addClass(submenu_classes) }}>
{% endif %}
{% for item in items %}
{%
set classes = [
'sidebar__item',
item.is_expanded ? 'sidebar__item--expanded',
item.is_collapsed ? 'sidebar__item--collapsed',
item.in_active_trail ? 'sidebar__item--active-trail',
]
%}
<li{{ item.attributes.addClass(classes) }}>
{{ link(item.title, item.url) }}
{% if item.below %}
{{ menus.menu_links(item.below, attributes, menu_level + 1) }}
{% endif %}
</li>
{% endfor %}
</ul>
{% endif %}
{% endmacro %}
Any help will be really appreciate it!
I have found the answer. First we set the classes:
{%
set submenu_classes = [
'sidebar__menu' ~ menu_name|clean_class ~ '--submenu',
'sidebar__menu--submenu-' ~ (menu_level + 1),
]
%}
Then using the logic like so:
{% if menu_level == 0 %}
<ul{{ attributes.addClass('container mx-auto', menu_classes) }}>
{% else %}
<ul{{ attributes.removeClass(menu_classes, 'sidebar__menu--submenu-' ~ (menu_level)).addClass(submenu_classes) }}>
{% endif %}

Storing HTML in a Twig Array

Ok I'm trying to add HTML code to a Twig array and ending up into problems. I'm not the most knowledgable when it comes to Twig so I'll need help. I'm getting this error when I try: Fatal error: Uncaught Twig_Error_Syntax: Arguments must be separated by a comma.
What am I doing wrong? https://pastebin.com/gEGLnCid
{% set myArray= [] %}
{% for product in products %}
You have to do it in two steps e.g.
{% set arr = [] %}
{% for i in 1..10 %}
{% set foo %}
{{ i * 10 }}
foo
bar
foobar
{{ i }}
{% endset %}
{% set arr = arr | merge([ foo, ]) %}
{% endfor %}
{% for val in arr %}
{{ val }}
{% endfor %}

Is it possible to concatenate a variable to an if statement in Twig?

I am trying to concatenate a variable to an array key to get access to certain values in Twig, but no success so far.
I have a large PHP array that has for example keys like this:
$array = [
...
...
...
'test_1' => $test_1,
'test_2' => $test_2
];
I tried the following in my Twig template:
{% for i in 1..2 %}
{% if array.test_{{ i }} != 0 %}
<div>Test</div>
{% endif %}
{% endfor %}
but that doesn't work.
Is there a way to access values like this in Twig?
Try this:
{% for i in 1..2 %}
{% if array['test_' ~ i] != 0 %}
<div>Test</div>
{% endif %}
{% endfor %}

Categories