How to write that path in string to be parsed - Twig - twig

I set links in this way in twig:
{% set link = '('~product.price~)'~product.name~'' %}
The problem is how to fill the link
{% set link = '('~product.price~)'~product.name~'' %}
This gives me the error: A hash key must be a quoted string, a number, a name, or an expression enclosed in parentheses (unexpected token "punctuation" of value "{"
{% set link = '('~product.price~)'~product.name~'' %}
Unexpected token "name" of value "restaurant" ("end of statement block" expected)
can you please help me :(
The link will also have an id argument.
The only thing I can think of is writing a filter which takes te id as argument and replaces the empty link, but I'm not quite sure it will work, and it doesn't seem like a good solution.

You should not use parentheses within {% %} blocks.
Furthermore, unless you have a specific reason for putting the whole markup for the link in a variable, you might want to consider creating links like this:
{{ product.name }}

Related

Can I mark a Twig variable equally as safe as a captured chunk of text?

Here's a Twig Fiddle. There you will notice this include file:
{# include.twig #}
{{ text }}
And this main file, with several attempts to mark a text variable as safe before passing it to the include file:
{# main.twig #}
1. Pass string with HTML
{{ include('include.twig', { text: '<p>Text</p>' }) }}
2. Pass string with HTML marked safe with raw filter
{{ include('include.twig', { text: '<p>Text</p>'|raw }) }}
3. Pass variable set with string literal marked safe with raw filter
{% set text = '<p>Text</p>'|raw %}
{{ include('include.twig', { text: text }) }}
4. Pass variable set with captured chunk of text
{% set text %}<p>Text</p>{% endset %}
{{ include('include.twig', { text: text }) }}
And here are the results of rendering this template:
1. Pass string with HTML
<p>Text</p>
2. Pass string with HTML marked safe with raw filter
<p>Text</p>
3. Pass variable set with string literal marked safe with raw filter
<p>Text</p>
4. Pass variable set with captured chunk of text
<p>Text</p>
The first attempt establishes the unwanted behavior of HTML escaping. Strategies 2 and 3 attempt to use the |raw filter to avoid the escaping, but both fail. Strategy 4, using the set tag to capture a chunk of text, works perfectly.
The documentation for the |raw filter says:
The raw filter marks the value as being "safe", which means that in an environment with automatic escaping enabled this variable will not be escaped if raw is the last filter applied to it:
The documentation for the set tag says:
If you enable automatic output escaping, Twig will only consider the content to be safe when capturing chunks of text.
Unless I'm missing something, these appear to be two different definitions of "safe", with the one mentioned under the set tag meaning super-extra-safe.
Is there any way to pass super-extra-safe HTML-containing string literals into a template, without polluting my file with ugly chunk-capturing set blocks?
The answer is no, you cannot because twig does not escape static expression. The raw filter is even ignored when compiling the template.
{% set hello = "<strong>Hello</strong>" %}
{% set hello = "<strong>Hello</strong>"|raw %}
The two lines above will compile in the same sourcecode
// line 1
$context["hello"] = "<strong>Hello</strong>";
// line 2
$context["hello"] = "<strong>Hello</strong>";
source
edit:
If you really want to do this, you could either write your own include/set node or add a filter which returns a Twig\Markup
<?php
$twig->addFilter(new \Twig\TwigFilter('safe', function($v) {
return new \Twig\Markup($v);
});
{% include 'text.html' with { 'text': 'Hello <strong>World</strong>'|safe, } %}
Hello World

Variables concatenation

After about an year of using Smarty i wanted to try Twig.
I am facing a problem concatenating a string and a variable to build dynamically the titles of the page when i switch the language.
In Smarty, the controller passes to the template the variables:
$title_it and $title_en
For the title of the page i do <title>{$title_{$lang}}</title> (where $lang is a global variable) and i can switch the values of the variables when i change the language.
I am not able to replicate this behaviour in Twig.
I tried the following methods without having success:
{{ title_ ~ {{ lang }} }} (I think Twig sees the variable "title_" doesn't exists.
'title_'~{{ lang }} (This prints 'title_it' and not it's content)
Is there a way to keep this logic and continuing to use this approach or do i have to handle the titles of the pages differenly?
Thanks a lot,
Manuel
The _context variable holds all variables in the current context, so you can do:
{{ _context['title_' ~ lang]|default }}
This is basically the same as using the attribute function:
{{ attribute(_context, 'title_' ~ lang)|default }}
I would personally use the former as it's more concise and in my opinion clearer.
The default filter is needed when the environment option strict_variables is set to true (the default value is false, but I prefer to set it to true to avoid accidental problems caused by e.g. typos), otherwise you'll get a Twig_Error_Runtime exception if the variable doesn't exist. For example, if you have variables title_en and title_it but try to output the variable title_de (which doesn't exist), you get that exception with the message Key "title_de" for array with keys "title_en, title_it, lang" does not exist.
A more verbose way to check the existence of a variable is to use the defined test:
{% if _context['title_' ~ lang] is defined %} ... {% endif %}
With the default filter you can also provide a default value:
{{ _context['title_' ~ lang]|default('Default title') }}
If you omit the default value (i.e. you do |default instead of |default('some value')), the default value will be an empty string.
See TwigFiddle
I think here the solutions which can fix your problems.
Controller code:
return $this->render('myTwig.html.twig', array(
'lang'=>'en',
'title_en'=>'English Title',
'title_it'=>'Italian Title'
));
Twig Code:
{% set myVar='title_'~lang %}
{{ attribute(_context, myVar) }}
This will display "English Title" on your page.
Here _context variable is a magic variable in twig where you can find all parameters which you passed from your controller to this twig.
attribute(_context,myVar)
it displays the value from passed parameters key to value.
Hope this will solve your problems.
Thanks
I would change the controller to pre-calculate the language dependent value for title before passing it to the twig template. If you are unable (unwilling) to do that, then ...
<title>{% if lang == 'it' %}{{ title_it }}{% else %}{{ title_en }}{% endif %}</title>

Dynamic variable in Twig, example?

I don't quite understand how the attribute function in Twig works. Can somebody help me with an example?
I have a field in a SQL that is named dynamic. I could be eg "field27", but I don't know the number, the number is saved in radio.id. I would like to do someting like this:
{% for radio in gruppeType.radios %}
<td><!-- value of "field" + radio.id--></td>
{% endfor %}
How can I use field + radio.id as the name of the twig-variable?
You can build the field name with a variable, then use it in the attribute function to access the data within the object/array. As example:
{% set fieldName = "field" ~ radio.id %}
{{ attribute(gruppeType, fieldName) }}
A working example can be seen in this twigfiddle
Hope this helps.

Twig Access Array Index?

Is it possible to directly access an array index from within a Twig template?
Here's my setup, using Silex:
return $app['twig']->render('template', array('numbers' => array('one', 'two', 'three')));
so can I do something like this?
{{numbers[0]}}
Just before posting this I realized, that's exactly what you can do, but as I didn't find the answer anywhere in the docs or google (correct me if I'm wrong), I've posted this anyway.
{{numbers[0]}}
The answer of Adam, is correct, only to make it clear and improve,
you can have access directly to array index
{{ myArray[0] }}
if you need to access in a loop
{% set arrayOfItems = ['ZERO', 'ONE'] %}
{% set myArray = ['APPLE', 'ORANGE'] %}
{% for oneItem in arrayOfItems %}
<p>{{ oneItem }} equals {{ myArray[loop.index0] }}</p>
{% endfor %}
in this example I used an array inside a non related loop so the result is:
ZERO equals APPLE
ONE equals ORANGE
Thats actually something what doesnt work for me when using Twig with shopware 6.
I try to access an object like
{{ page.cart.lineItems.elements[0].quantity }}
what will lead into a parsing error of the Twig Template
I can use
{{ page.cart.lineItems.elements | first }}
to get the first Element, but dont know how i can then access a property of this first element

Does Twig allow assignments in conditions and how?

As per title, does Twig allow assignments in conditional and how? I know that not all people like these assignments but sometimes they can be very helpful.
This:
{% if (name = attribute(mappings, property)) is defined %}
{% else %}
{% set attrs = attrs|merge(['%s="%s"'|format(name, value)]) %}
{% endif %}
...is not working and gives me and error:
An opened parenthesis is not properly closed. Unexpected token
"operator" of value "=" ("punctuation" expected with value ")") in
::tooltips.html.twig at line 29.
Your twig error is because of this line
{% if (name = attribute(mappings, property)) is defined %}
Twig doesn't like assignment-in-condition expressions, and personally neither do I, it's just a lazy shortcut and the potential issues isn't worth saving a few keystrokes.
But I have to now admit that I'm confused by what this is supposed to do. You're attempting to capture the result of attribute(mappings, property) into name but if that fails, only then do you do something using name, but by then name won't have a value unless it's been defined elsewhere in the template.

Resources