Displaying table in twig dynamically
Asked Answered
M

3

8

I'm trying to display all the users in my User object without knowing the structure of the object (so I can use the same table to display other collection of objects as well).

This is what it would look 'statically':

<table>
    <tr>
        <td>id</td>
        <td>username</td>
    </tr>
    {% for item in entities %}
        <tr>
            <td>{{ item.id }}</td>
            <td>{{ item.username }}</td>
        </tr>
    {% endfor %}
</table>

What i would want to do is something as follows (this is just to display what I'm trying to do, but its not even close to working):

<table>
    <tr>
        {% for property_title in entities.item[0] %} 
            <td>{{ property_title }}</td>
        {% endfor %}
    </tr>
    {% for item in entities %}
        <tr>
            {% for property in item %}
                <td>{{ property.value }}</td>
            {% endfor %}
        </tr>
    {% endfor %}
</table>

Result should be something as follows:

<table>
    <tr>
        <td>id</td>
        <td>username</td>
    </tr>
    <tr>
        <td>1</td>
        <td>Mike123</td>
    </tr>
    <tr>
        <td>2</td>
        <td>jesica2</td>
    </tr>
</table>

PD: this is my first post, so apologies if I missed something.

Maurizio answered 4/2, 2014 at 19:17 Comment(6)
And what does not work?Saturninasaturnine
He obviously wants to loop over all (public?) properties and/or getter-methods of an object inside a generic collection containing only objects of the same type ... but he doesn't know how to get their names (and count) in order to create the loop. There is no twig filter/function available to get these directly but there are workarounds ...Lauzon
you got it. Thanks for explaining. I just made up the code based on how I thought it could work for the purpose of explanation.Maurizio
Note that twig allows to do item.username, even if username is private/protected. Not sure if it makes a difference though.Maurizio
yes it does make a difference , twig till try to access the property (i.e. $property) first ... and if that doesn't work (i.e. due to the property being private/protected ) ... try to invoke the corresponding getter method getProperty() when using the . notation. So user.name does fail $name is private/protected and there is no getName() function.Lauzon
Got it. I didn't know internally it did that, but it makes sense. I have all protected properties with respective getters and setters for all the elements to display in this case.Maurizio
L
5

Make a twig extension that returns a list of the fields you want, that way you can use php to get the fields. After that use twig's attribute function

{{ attribute(object, fields) }} to call the getters on the object

docs:

http://symfony.com/doc/current/cookbook/templating/twig_extension.html http://twig.sensiolabs.org/doc/functions/attribute.html

example:

{% set temp = entities|first %}
{% set fields = getObjectFields(temp) %}
<tr>
{% for property_title in fields %} 
    <td>{{ property_title }}</td>
{% endfor %}
</tr>
{% for item in entities %}
    <tr>
        {% for field in fields %}
            <td>{{ attribute(item, field) }}</td>
        {% endfor %}
    </tr>
{% endfor %}
Lamonicalamont answered 5/2, 2014 at 1:20 Comment(5)
Awesome. I'll try it and let you know!Maurizio
Ok. So I tested it and its kind of there. I'll post the full solution once I have it. The issue I'm having is that {{ attribute(item, property) }} works only with string and int. When there is a date or a reference to another object it blows up. Any suggestion?Maurizio
DATE ERROR (ContextErrorException: Catchable Fatal Error: Object of class DateTime could not be converted to string ) REFERENCE ERROR(ContextErrorException: Catchable Fatal Error: Object of class Proxies ABC could not be converted to string)Maurizio
create another twig extension or filter called sanitize and have it check if the value is of date time, if it is of datetime return a formatted string date, otherwise have return the value givenLamonicalamont
Yup. I did. I was able to make this work now pending some minor cleanup. Good to see how twig extensions work. Eventually I realized this solution is not realistic for my use and I will create a table per entity as per how initially I did. I dont have enough rep points, but THIS IS THE CORRECT ANSWER. I'll come back once I earn enough points to mark it.Maurizio
S
5

Modifying Derick F's answer, for simplicity you could use the keys to get the field name instead of using a twig extension. The answer below doesn't require the 'fields' variable being set in a extension class.

{% set temp = entities|first %}

<tr>
{% for property_title in temp|keys %} 
    <td>{{ property_title }}</td>
{% endfor %}
</tr>
{% for item in entities %}
    <tr>
        {% for field in temp|keys %}
            <td>{{ attribute(item, field) }}</td>
        {% endfor %}
    </tr>
{% endfor %}

This requires the headers to be stored in the key of the entity. For more complex array structures and more flexibility a twig extension may be necessary.

To check for datetime objects and convert to a string accordingly you can look at the thread below which doesn't require an extension also:

Check if a variable is a date with Twig

Steelworker answered 7/12, 2016 at 13:5 Comment(0)
S
0

Iterating on @Richard-H's answer, here a fully generic version that works with SQL requests such as SELECT * FROM table:

{% set columns = table|first %}

<table role="grid">
  <thead>
    <tr>
      <!--th scope="col">#</th>-->
{% for title in columns|keys %}
      <th scope="col">{{ title }}</th>
{% endfor %}
    </tr>
  </thead>
  <tbody>
{% for row in table %}
    <tr>
      <!--<th scope="row">1</th>-->
{% for cell in row %}
      <td>{{ cell }}</td>
{% endfor %}
    </tr>
{% endfor %}
  </tbody>
</table>
Shifty answered 11/6, 2023 at 6:58 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.