Twig iterate over object properties

Viewed 63066

I read in the Twig documentation that it is possible to iterate over an associative array in the following manner:

{% for key, value in array %}  
 {{key}}  
 {{value}}  
{% endfor %}  

I was wondering whether this is possible for objects of type stdClass as well.

I would have expected Twig to iterate over the property values of the object taking the property names as keys. Instead, the instruction block contained in the for loop is not executed at all.

8 Answers

In case this helps someone else. You can have Twig iterate the properties of your object provided you implement PHP's Iterator interface.

In my case I have a generic object the uses the magic methods __get(), __set(), __isset(), and __unset() while storing key value pairs in a private array. This works fine in Twig until you want to iterate over the object using something like this

<ul>
{% for prop, value in object %}
    <li>{{prop|replace({'_': ' '})|title}}</li>
{% endfor %}
</ul>

To make it work I had to implement the Iterator interface. Then the above code worked perfectly.

Now because of the magic __get() the property names are not case sensitive either so each of these work also.

<ul>
{% for object in arrayOfObjects %}
    <li>{{ object.property }}</li>
    <li>{{ object.Property }}</li>
    <li>{{ object.PROPERTY }}</li>
{% endfor %}
</ul>
  {% for key, item in content.field_invoice_no if key|first != '#' %} <tr>
      <td>{{ item }}</td>
       <td> {{ content.field_invoice_date[key]  }}     </td>
       <td> {{ content.field_invoice_price[key]   }}       </td>
       </tr>
    {% endfor %}

Iterating object over key value

I finally figured out a vanilla Twig solution. I make use of the reduce filter to transform an object to an array.

Let's say we get an address object from the backend and want to convert it into an associative array in order to render each line separate.

{% set address = { firstName: 'Stack', lastName: 'Overflow', address: 'here', zipCode: '1234' } %}

We reduce the keys and add them to an empty array

{% set addressLines = address|keys|reduce((carry, key) => carry|merge({ (key): attribute(address, key) }), []) %}

This results in:

[ 'firstName' => 'Stack', 'lastName' => 'Overflow', 'address'=> 'here', 'zipCode' => '1234' ]

Now we can render this in a for loop

{% set allowedKeys = ['firstName', 'lastName', 'address'] %}
{% for key, line in addressLines|filter((v, k) => k in allowedKeys) %}
    {{ line }}<br/>
{% endfor %}

add function array to twing and call in template array(value)

$loader = new \Twig\Loader\FilesystemLoader('templates/'.$path);
$twig = new \Twig\Environment($loader,['debug' => true]);
$function_2 = new \Twig\TwigFunction('array', function ($value) {
    return (array)$value;
});
$twig->addFunction($function_1);
Related