I have an array of user records (0 indexed, from a database query), each of which contains an array of fields (indexed by field name). For example:
Array
(
[0] => Array
(
[name] => Fred
[age] => 42
)
[1] => Array
(
[name] => Alice
[age] => 42
)
[2] => Array
(
[name] => Eve
[age] => 24
)
)
In my Twig template, I want to get all the users where the age field is 42 and then return the name field of those users as an array. I can then pass that array to join(<br>) to print one name per line.
For example, if the age was 42 I would expect Twig to output:
Fred<br>
Alice
Is this possible to do in Twig out of the box, or would I need to write a custom filter? I'm not sure how to describe what I want in a couple of words so it may be that someone else has written a filter but I can't find it by searching.
Final solution was a mix of what has been posted so far, with a couple of changes. The pseudocode is:
for each user
create empty array of matches
if current user matches criteria then
add user to matches array
join array of matches
Twig code:
{% set matched_users = [] %}
{% for user in users %}
{% if user.age == 42 %}
{% set matched_users = matched_users|merge([user.name|e]) %}
{% endif %}
{% endfor %}
{{ matched_users|join('<br>')|raw }}
merge will only accept an array or Traversable as the argument so you have to convert the user.name string to a single-element array by enclosing it in []. You also need to escape user.name and use raw, otherwise <br> will be converted into <br> (in this case I want the user's name escaped because it comes from an untrusted source, whereas the line break is a string I've specified).
In twig you can merge the for ( .... in ....) with the if condition like :
{% for user in users if user.age == 42 %}
{{ user.name }}{{ !loop.last ? '<br>' }}
{% endfor %}
Edit: This syntax is deprecated, and we are advised to use |filter as a replacement for the for...if syntax.
Twig Filter: filter (The name of the filter is filter)
Twig Deprecated Features
You can apply a filter on the array you apply for loop on, like this:
{% for u in user|filter((u) => u.age == 42) -%}
<!-- do your stuff -->
{% endfor %}
{% for user in users %}
{% if user.age == 42 %}
{{ user.name|e }}<br>
{% endif %}
{% endfor %}
in alternative you can create an array of elements
{% set aUserMatchingCreteria %}
{% for user in users %}
{% if user.age == 42 %}
{% aUserMatchingCreteria = aUserMatchingCreteria|merge(user.name) %}
{% endif %}
{% endfor %}
{{ aUserMatchingCreteria|join('<br>') }}
Since Twig 2.10, the recommended way to conditionally exclude array elements is the filter filter. As was noted in some previous answers, loop.last has some issues, but you can simply flip the logic and use loop.first, which will work consistently:
{% for user in users|filter((u) => u.age == 42) %}
{{ loop.first ?: '<br/>' }}
{{ user.name|e }}
{% endfor %}
I want to add key and value into array in twig file. But I am facing following issue "Twig_Error_Syntax: A hash key must be a quoted string or a number"
{% set phoneCount = 0 %}
{% set phoneNumbers = {} %}
{% for currPhone in currBroker.phones %}
{% if (currPhone.type == 'Work' or currPhone.type == 'Mobile') and phoneCount <= 2 and currPhone.number !='' %}
{% set phoneCount = phoneCount + 1 %}
{% set phoneNumbers = phoneNumbers|merge({ currPhone.type:currPhone.type }) %}
{% endif %}
{% endfor %}
{{ phoneNumbers|print_r }}
I just need the syntax of merging key and value into array.
I tried by giving static inputs and its works
{% set phoneNumbers = phoneNumbers|merge({ 'work':'(011)112-1233' }) %}
But its not working for dynmic input. Please help!!
You have to wrap your key in braces :
{% set phoneNumbers = phoneNumbers|merge({ (currPhone.type) : currPhone.type }) %}
Tested and working example :
{% set currPhone = {type: 'test'} %}
{% set phoneNumbers = {} %}
{% set phoneNumbers = phoneNumbers|merge({ (currPhone.type) : currPhone.type }) %}
{% dump(phoneNumbers) %}
I get :
array:1 [▼
"test" => "test"
]
I'm trying to save some values into a multiarray on Twig, but for some reason, it's not working.
I want to save in an array a list of users and some values of them. I am using merge function to create the multi array.
{% for result in results %}
{% set users = users|merge({ ('name'~loop.index):result.name,('age'~loop.index):result.age,('credits'~loop.index):result.credits}) %}
{% endfor %}
Try this out :
{% set users = [] %}
{% for result in results %}
{% set users = users|merge([{ 'name' : result.name, 'age' : result.age, 'credits' : result.credits }]) %}
{% endfor %}
I have a list of tasks stored in the doctrine database which I return like this:
$task = $this->getDoctrine()
->getRepository('SeotoolMainBundle:Tasks')
->findAll();
return array('form' => $form->createView(), 'message' => '', 'list_tasks' => $task);
In one row the ID of and entry from a second table is stored. I don't want only output the ID but also the Name, Description and so on stored in the other table. With normal PHP & MYSQL this would be done by using JOIN - how can I do it with Symfony & TWIG ?
Twig Output:
{% for task in list_tasks%}
{{ task.Id }}
{{ task.TaskTitle }}
{{ task.TaskDescription }}
{{ task.TaskTypes }} /* Here I want not only get the ID but also other fields stored in the database with TaskType ID = task.TaskTypes */
{{ task.User }} /* Here I want not only get the ID but also other fields stored in the database with User ID = task.User */
{% endfor %}
I am assuming that TaskType and User are entities, which belong within the Tasks entity. In which case, try the following. Note that I am getting just one task with id of 1 in my example:
$task = $this->getDoctrine()->getRepository('SeotoolMainBundle:Tasks')->find(1);
$taskTypes = $task->getTaskTypes();
$user = $task->getUser();
return array(
'form' => $form->createView(),
'message' => '',
'list_tasks' => $task,
'task_types' => $taskTypes,
'user' => $user,
);
And in your Twig:
{% for task_type in task_types %}
{{ task_type.Id }} // or whatever fields a task_type has
{% endfor %}
And the same for the user
Edit:
As you are wanting to have ALL tasks processed at once, I wonder if simply the following willl work:
{% for task_list in task_lists %}
{% for task_type in task_list.taskType %}
{{ task_type.Id }} // or whatever fields a task_type has
{% endfor %}
{% endfor %}
I did it now on this way, but I dont think, that it is 100% correct and conform.
How to do better? Luckily this works for me actually:
$task = $this->getDoctrine()
->getRepository('SeotoolMainBundle:Tasks')
->findAll();
$taskTypes = $this->getDoctrine()
->getRepository('SeotoolMainBundle:TaskTypes')
->findAll();
$user = $this->getDoctrine()
->getRepository('SeotoolMainBundle:User')
->findAll();
return array(
'form' => $form->createView(),
'message' => '',
'list_tasks' => $task,
'list_task_types' => $taskTypes,
'list_user' => $user
);
TWIG:
{% for task in list_tasks %}
Task ID: {{ task.ID }} <br/>
{% for type in list_task_types if type.id == task.tasktypes %}
{{ type.tasktypetitle }} <br/>
{% endfor %}
{% for user in list_user if user.id == task.user %}
{{ user.username }} <br/>
{% endfor %}
<hr/>
{% endfor %}
How can I set member of an already existing array from Twig?
I tried doing it next way:
{% set arr['element'] = 'value' %}
but I got the following error:
Unexpected token "punctuation" of value "[" ("end of statement block"
expected) in ...
There is no nice way to do this in Twig. It is, however, possible by using the merge filter:
{% set arr = arr|merge({'element': 'value'}) %}
If element is a variable, surround it with brackets:
{% set arr = arr|merge({(element): 'value'}) %}
I ran into this problem but was trying to create integer indexes instead of associative index like 'element'.
You need to protect your index key with () using the merge filter as well:
{% set arr = arr|merge({ (loop.index0): 'value'}) %}
You can now add custom index key like ('element'~loop.index0)
If initialization only need:
{% set items = { 'apple': 'fruit', 'orange': 'fruit', 'peugeot': 'unknown' } %}
I have tried #LivaX 's answer but it does not work , merging an array where keys are numeric wont work ( https://github.com/twigphp/Twig/issues/789 ).
That will work only when keys are strings
What I did is recreate another array ( temp) from the initial array (t) and make the keys a string , for example :
{% for key , value in t%}
{% set temp= temp|merge({(key~'_'):value}) %}
{% endfor %}
t keys : 0 , 1 , 2 ..
temp keys : 0_, 1_ , 2_ ....
You can also use the following syntax:
{% set myArray = myArray + myArray2 %}
Just use this like {% set arr={'key':'value'} %} (with no blank space after the :), it works well.
But when I use it inside a for loop, to make it an array, it does not work outside of the for scope.
{% for group in user.groups %}
{% set foo={'loop.index0':'group.id'} %}
{% set title={'loop.index0':'group.title'} %}
{{ title }} //it work
{% else %}
{% set foo={'0':'-1'} %}
{% set title={'0':'未分组'} %}
{% endfor %}
{{ title }} //it does not work, saying title is not defined
{% set links = {} %}
{# Use our array to wrap up our links. #}
{% for item in items %}
{% set links = links|merge({ (loop.index0) : {'url': item.content['#url'].getUri(), 'text': item.content['#title']} }) %}
{% endfor %}
{%
set linkList = {
'title': label,
'links': links
}
%}
{% include '<to twig file>/link-list.twig'%}
Thanks for this thread -- I was also able to create an array with (loop.index0) and send to twig.
I've found this issue very annoying, and my solution is perhaps orthodox and not inline with the Twig philosophy, but I developed the following:
$function = new Twig_Function('set_element', function ($data, $key, $value) {
// Assign value to $data[$key]
if (!is_array($data)) {
return $data;
}
$data[$key] = $value;
return $data;
});
$twig->addFunction($function);
that can be used as follows:
{% set arr = set_element(arr, 'element', 'value') %}
Adding my answer in case anyone needs to update the array when merge doesn't work because it just appends to the end of an array instead of providing the ability to change an existing value.
Let's say you have an array words_array like below:
Object {
0: "First word"
1: "Second word"
2: "Third word"
}
In order to update "Second word", you can do the following:
{% set words_array = {(1): 'New word'} + words_array %}
The resulting array would be:
Object {
0: "First word"
1: "New word"
2: "Third word"
}
You can take it one step further if you are using a loop and use the loop.index0 variable something like the following:
{% for word in words_array %}
{% if word == 'Second word' %}
{% set words_array = {(loop.index0): 'New word'} + words_array %}
{% endif %}
{% endfor %}
You can declare the array as follows
{% set arr = [{'element1': 'value1','element2' : 'value2'},{'element1': 'value1','element2' : 'value2'},{'element1': 'value1','element2' : 'value2'}] %}
I had a multi dimension array. The only solution I could find out is create a new temporary array and update/add the information, which was further passed on to another twig function.
I had this problem sometime ago. Imagine you have an array like this one:
data = {
'user': 'admin',
'password': 'admin1234',
'role': 'admin',
'group': 'root',
'profile': 'admin',
'control': 'all',
'level': 1,
'session': '#DFSFASADASD02',
'pre_oa': 'PRE-OA',
'hepa_oa': 'HEPA-OA',
'pre_ra': 'HEPA-RA',
'hepa_ra': 'HEPA-RA',
'deodor_ra': 'DEODOR-RA'
}
So, you want to show this data in two rows, but remove the password from that list. To this end, split in 2 arrays will be easy with the slice filter. However, we have to remove the password. For that reason, I'm using this snippet. The idea is to put all the elements lesser than the data elements size divided by 2. To calculate this we use the filter length. Now to get the index of the current element we user loop.index. And finally we *push an associative element in the left or right array. An associative array has two components key and value. To reference an array key in twit we operator () and we use the merge filter to push into the array as shown here {% set left_list = left_list|merge({ (key): value }) %}
This is the complete solution.
{% set left_list = {} %}
{% set right_list = {} %}
{% set limit = data|length // 2 %}
{% for key, value in data|cast_to_array %}
{% if key != 'password' %}
{% if loop.index <= limit %}
{% set left_list = left_list|merge({ (key): value }) %}
{% else %}
{% set right_list = right_list|merge({ (key): value }) %}
{% endif %}
{% endif %}
{% endfor %}
{% for key, value in left_list %}
<p>
<label for="{{key}}">{{key}}</label>
<input type="text" name="{{key}}" id="{{key}}" value="{{value}}"
class="text ui-widget-content ui-corner-all">
</p>
{% endfor %}