How do you translate array items and join them? - twig

Using twig, how can I translate all items in an array and join them with a slash?
Do I have to use an additional variable or is there a cleverer method?
For the moment, I'm doing something like this:
{% set labels = [] %}
{% for feature in menu_item.features %}
{% set labels = labels|merge([feature|trans([], 'features')]) %}
{% endfor %}
{{ labels | join(' / ')}}
It sucks.

Why not just output the content while you're looping ?
{% for feature in menu_item.features %}
{% if loop.index0 > 0 %}/{% endif %}
{{feature|trans}}
{% endfor %}

Maybe I'm late to the party, but you can now do this easily with the map filter:
{{ menu_item.features|map(feature => feature|trans)|join(' / ') }}
See documentation:
Twig >v1.41: https://twig.symfony.com/doc/1.x/filters/map.html
Twig >v2.10: https://twig.symfony.com/doc/2.x/filters/map.html
Twig v3.x: https://twig.symfony.com/doc/3.x/filters/map.html

Not everything should be done within the "view".
This type of code is probably much better placed within your controller logic and then passed into the view as the merged+joined result. Because in your example all you're doing is compiling a result which can much more easily be done within code.

Related

Twig strip textarea and create array

I'm trying to strip some text from a value and afterwards create an array from it with the stripped values.
I'm having troubles to strip the text value.
I can only do this on the frontend since I have no access to the backend (SaaS platform)
In below example value.value (originally a textarea) returns the following text:
[185047078]1x something - Type 1
[415533322]1x something - something
[152890667]1x something 500x500 mm
I want to strip the text so I have [185047078], [415533322], [152890667] left or without the brackets.
Normally in JS you would do something like:
hide_ids = txt.match(/[^\]\[]+(?=\])/g)
However it need to be done in Twig.
Afterwards I want to push the values into an array hide_ids.
{% set hide_ids = [] %}
{% if product.custom %}
{% for custom in product.custom %}
{% if 'Some title' in custom.title %}
{% for value in custom.values %}
{% set hide_this_id = value.value %}
{% if hide_this_id matches '{/[^\]\[]+(?=\])/g}' %}
{% set hide_ids = hide_ids | merge([hide_this_id]) %}
{% endif %}
{% endfor %}
{% endif %}
{% endfor %}
{% endif %}
{% set hidden = false %}
{% if id in hide_ids %}
{% set hidden = true %}
{% endif %}
What is the equivalent of match in Twig? I also tried replace but I just can't get that text stripped.
Any help greatly appreciated!
You could go with some simple string functions.
{% for id in ids %}
{{ id | split(']', 2)[0] | replace({'[': '',}) }}
{% endfor %}
demo
split is the explode of twig. This will separate your string in chased based on the ] character. The 2nd parameter (2) ensures there will only be maximum 2 parts in the array.
replace is just str_replace
If you wanted to solve this with regex you would either need to write a function/filter with preg_match or install an extension like this

TWIG / GravCMS: Use loop-variable of for-loop inside modular template

Here is my current code:
{% for module in page.collection() %}
{% set index = loop.index %}
{{ module.content|raw }}
{% endfor %}
I'd like to access index inside the module.html.twig, or even better, the entire loop variable.
How do I do that?
I found it myself:
{% for module in page.collection() if not module.header.visible is same as(false) %}
{% include module.template ~ '.html.twig' with {'page':module, 'loop':loop} %}
{% endfor %}
This loop willautomatically grab the template which is linked to the modular page and pass the required variables down. Also, the loop will only include modular subpages which are not hidden. Great, isn't it?

How to evaluate twig expression

I've bend over backwards to solve this one, but no luck yet.
{% for field in fields %}
{{dump(form.children.~(field.label)~.vars.value)}}
{% endfor %}
U see the above code in twig? How do I get twig to evaluate (field.label) first and then evaluate the rest of the expression((form.children.Age.vars.value)). For now it throws the twig syntax error
Expected name or number.
Any ideas?
You can use:
{% for field in fields %}
{{ dump(form.children[field.label].vars.value) }}
{% endfor %}
Cheers

How to use Twig_Markup object type in an If statement

I want to reuse pretty heavy logic only code a few times, in php I would use a function, but in twig I went with a solution from this old question.
In short, I use a macro like that:
{% import _self as test %}
{% macro check() %}
{{ test }}
{% endmacro %}
{% set v = test.check() %}
{% if v == 'test' %}
this should display
{% endif %}
Here is a fiddle: https://twigfiddle.com/kyv3zr/2
The problem is that v is a Twig_markup object. It doesn't seem to have any public properties. Running dump on it gives me this:
object(Twig_Markup)#1244 (2) { ["content":protected]=> string(13) " 1 " ["charset":protected]=> string(5) "UTF-8" }
How do I use it in an if statement?
Or is there a better way of storing a logic only code for reuse across templates?
If the object is called v then the dump seems to show it has a content value, so try:
{% if v.content == '1' %}
{# do something here #}
{% endif %}
not certain though, but try it.
EDIT #2 - based on comments question.
So I guess if you want to use v in an if statement, you would use it like so:
{% if v == '1' %}
{# do something here #}
{% endif %}
This presumes it does equal to "1".

How to map scalar twig filter to array

I have a simple array of floats. And I need to show it as comma separated string.
{{ arr|join(', ') }}
is bad solution because of excessive insignificant accuracy.
{% for val in arr %}
{{val|number_format(2)}},
{% endfor %}
is bad because of extra comma at the end.
I would like to do something like this:
{{ arr|map(number_format(3))|join(', ') }}
but I have not found filter map or similar filter it Twig.
Аnd I don't know how to implement such filter.
Why not use the loop variable?
{% for val in arr %}
{{val|number_format(2)}}
{% if not loop.last %}, {% endif %}
{% endfor %}
Quick Answer (TL;DR)
This question relates to higher-order functions
Map is a higher-order function
(see eg) https://en.wikipedia.org/wiki/Map_(higher-order_function)
(see eg) https://en.wikipedia.org/wiki/Higher-order_function
Detailed Answer
Context
Twig 2.x (latest version as of Wed 2017-02-08)
Problem
Scenario: DeveloperGarricSugas wishes to apply higher-order function(s) to a Twig variable
Higher order functions allow any of various transformations on any Twig variable
Twig support for higher-order functions is limited
Nevertheless, there exist addon libraries for this
(see eg) https://github.com/dpolac/twig-lambda
Custom filters or functions can also be used to simulate this
Example
DeveloperGarricSugas starts with a sequentially-indexed array
transform from BEFORE into AFTER (uppercase first letter)
{%- set mylist = ['alpha','bravo','charlie','delta','echo'] -%}
BEFORE:
['alpha','bravo','charlie','delta','echo']
AFTER:
['Alpha','Bravo','Charlie','Delta','Echo']
Solution
{%- set mylist = mylist|map(=> _|capitalize) -%}
Pitfalls
Twig higher-order functions limited support comes from addon-libraries
The above solution does not work with native Twig
See also
https://twigfiddle.com/rsl89m
https://github.com/dpolac/twig-lambda
Quick Answer (TL;DR)
alternate approach is to use Twig loops (workaround)
Workaround
{%- set aaold = [1.234,234.56,11.222,22.333] -%}
{%- set aanew = [] -%}
{% for item in aaold -%}
{{ aanew|merge(item|number_format(3)) }}
{% endfor %}
{{ aanew | join(', ') }}
Pitfalls
requires use of array|merge
alternative to array|merge ... loop.first or loop.last as specified here
requires addition of loop control structure that means extra code bloat

Resources