Does Twig have a null coalesce operator? - twig

I'm using the Twig PHP template engine.
Is there an operator available which will output first non-empty value (coalesce)?
For example (using PHP pseudocode):
{{ title ?: "Default Title" }}
I know I could do something like this, but it's a bit long-winded:
{% if title %}{{ title }}{% else %}{{ "Default Title" }}{% endif %}

The null-coalescing operator was formally introduced in Twig 1.24 (Jan. 25, 2016).
* adding support for the ?? operator
Which means it's now possible to do this...
{{ title ?? "Default Title" }}
You can even chain them together, to check multiple variables until a valid non-null value is found.
{{ var1 ?? var2 ?? var3 ?? var4 }}

Yes, there is this filter called default. You can apply it to your code like below:
{{ title|default("Default Title") }}

As of Twig 1.12.0, it does have the ?: operator, but it's not really "null coalescing". It checks for truthiness, not just nulls, thus 0 ?: 1 would come out 1.
Documentation

Related

Displaying a link in a Twig temple

It seems easy, but I encounter a weird behavior.
In a twig file :
{% set my_html = 'Hello world' %}
{{- true is not same as(false) ? (true is same as(false) ? ('1'~my_html)|raw : ('2'~my_html)|raw) -}}
The printed part is ('2'~my_html)|raw and it works fine : I see a real link.
Output is :
2Hello world
Now, it works only because I apply the raw filter to ('1'~my_html)... Try this :
{% set my_html = 'Hello world' %}
{{- true is not same as(false) ? (true is same as(false) ? ('1'~my_html) : ('2'~my_html)|raw) -}}
And it will display : 2Hello world
I don't understand why I need to apply a filter on something else to get the expected result ? Is it a bug ?
This is a documented behavior of the raw filter. I quote the note from that page:
Be careful when using the raw filter inside expressions:
{% autoescape %}
{% set hello = '<strong>Hello</strong>' %}
{% set hola = '<strong>Hola</strong>' %}
{{ false ? '<strong>Hola</strong>' : hello|raw }}
does not render the same as
{{ false ? hola : hello|raw }}
but renders the same as
{{ (false ? hola : hello)|raw }} {% endautoescape %}
The first ternary statement is not escaped: hello is marked as being
safe and Twig does not escape static values (see escape). In the
second ternary statement, even if hello is marked as safe, hola
remains unsafe and so is the whole expression. The third ternary
statement is marked as safe and the result is not escaped.
And a comment on a github issue clarifies that the concatenation operator marks your string as unsafe. So in your case
{% set my_html = '<' %}
{# ('1'~my_html) is not safe, so the whole expression is not #}
{{ false
? ('1'~my_html)
: ('2'~my_html)|raw
}}
includes two strings: a safe one, ('2'~my_html)|raw) and an unsafe one, ('1'~my_html) (because it does not apply the raw filter), so as the note from raw documentation says, the whole expression stays unsafe and autoescaping is applied. But in the other case when both strings are marked safe, the whole expression becomes safe and the autoesaping is not applied:
{% set my_html = '<' %}
{# now both strings are safe, so is the whole expression #}
{{ false
? ('1'~my_html)|raw
: ('2'~my_html)|raw
}}
This is not a bug but, due to the fact the default settings of twig will autoescape variables.
You can read more about it in the documentation.

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 translate dynamic value/string from database

Explanation:
I pull these values from my local database and try to display them on the front-end. The issue is, that I have 2 languages that I need to cater to.
Example:
{% if activeLocale == "si" %}
{{ record.estate_type_SI|raw }}
{% elseif activeLocale == "en" %}
{{ record.estate_type_EN|raw }}
{% endif %}
This works, but when I have multiple items it gets gruesome because I have to write everything down two times. What this does is that depending on the language a value from a different column in the database is pulled.
I am wondering if I can do something similar to this:
{{ record.estate_type_{{"SI"|trans}}|raw }}
I will gladly buy you a beer if you can help me out with this.
Cheers!
EDIT: Variables
Using attribute , you can access a property of an object in a dynamic way. Then you just have to use upper filter to match what you need.
{{ attribute(record, 'estate_type_'~ activeLocale|upper)|raw }}

concat string and variable in twig

I have this in my template
{{ ad.title_de }}
Now I'm incorporating multiple languages, so 'title_de' has to change
I also have a variable 'tld' which is one of de, en , fr
So I'd like to have something like
{% if tld == 'fr' %}
{{ ad.title_fr }}
etc
Any ideas?
Try using the attribute function.
http://twig.sensiolabs.org/doc/functions/attribute.html
attribute(ad, 'content_'~tld) should work.
Try with this:
{{ ad["title_" ~ tld] }}

Resources