2

I'm currently creating some standard templates which should help to find good defaults for matrix output. This is where I stumpled upon the somehow weird structure of table data.

Here's a simplified structure of what I want to build:

<table>
{% for row in table %}
  <tr>
  {% for data in row %}
    <td>{{ data }}</td>
  {% endfor %}
  </tr>
{% endfor %}
</table>

Outputting table content like this:

{% for row in table %}
  {{ dump(row) }}
{% endfor %}

shows me that each row has double entries:

array(6) {
  ["col1"]=> string(7) "Hamburg"
  ["col2"]=> string(7) "1730000"
  ["col3"]=> string(7) "2600000"

  ["city"]=> string(7) "Hamburg"
  ["population"]=> string(7) "1730000"
  ["urbanPopulation"]=> string(7) "2600000"
}

While this might make sense in some cases, I don't get it. Why is this behavior and in which cases could the convention "col1", "col2", etc. make sense?

On first sight, this choice is very unfortunate as it prevents dynamic table output. Sure, you could use hacks like loop.index to target these, but that makes the code very unreadable and ugly.

Does anyone have the same issue with this?

4

The data is not stored twice in the database. I suspect that craft adds the additional array items when prepping the data, so that they can be referenced by either column number or column title.

But with a little finesse, you might be able to do something like the following, using key, value in the for loop and twig's slice filter:

<table>
    {% for row in entry.myTableFieldHandle %}
        <tr>
            {% for key, data in row if key|slice(0,3) == 'col' %}
                <td>{{ data }}</td>
            {% endfor %}
        </tr>
    {% endfor %}
</table>
  • Hey Douglas, thanks - this is what I was thinking. So at the moment there seems to be no way to circumvent hacks like for key, data in row if not key|[0,2] == "col" , right? That's a pity. I will test right away and mark as solved then. I'll try to get some information if this might be changed in the future. Anybody actually seeing the benefit of colX as you can reference the column number via the array itself? – Max Feb 2 '15 at 7:26
  • Fyi... I expanded this code to output the entire table including headers. – Douglas McDonald Feb 3 '15 at 21:11
  • Yes, thanks. I marked it as solved, as it is as good as it can get - without having to use a plugin (which is not bad, but not the way I wanted to solve it - in reference to carlcs' answer). – Max Feb 5 '15 at 7:47
  • Using this code as shown will fail if you have any field name starting with "col" like "color". – Nazareno Lorenzo Feb 9 '17 at 19:12
2

Max - it looks like there might be an issue in how you are looping through the table. Table data is stored as JSON in the database, so calling entry.myTable should give you an array of rows, with each row as a sub-array of values.

From your example:

{% if entry.myTable | length %}
    <table>
        <thead>
            <td>City</td>
            <td>Population</td>
            <td>Urban Population</td>
        </thead>
        <tbody>
        {% for row in entry.myTable %}
            <tr>
                <td>{{row.city}}</td>
                <td>{{row.population}}</td>
                <td>{{row.urbanPopulation}}</td>
            </tr>
        {% endfor %}        
        </tbody>
    </table>
{% endif %}

will check to see if the table field exists and has data, then will go through each row, returning values by row.columnName.

Hope this helps!

  • hey, thanks for the reply - sure, you're right. But this way I can't write a template which I can use dynamically - as I'd have to write the handles manually. The loop actually does work, but I get DOUBLE output, because not only {{row.columnName}} exists, but also {{row.col1}}. This is the cause why, if I loop through dynamically I get 2x Hamburg. You see the problem? – Max Feb 1 '15 at 15:42
  • What are you trying to dynamically pull, the table headers? – Aaron Berkowitz Feb 1 '15 at 15:45
  • I want to dynamically output the table data, no matter which column names the user creates. This would actually work, if the Craft team would not store the data twice. And I don't know why they did it. array(2) { ["col1"]=> string(7) "Hamburg", ["city"]=> string(7) "Hamburg" } so you can query by using {{ row.col1 }} and {{ row.columnName }} but you cant loop through dynamically, because you get all values twice. – Max Feb 1 '15 at 15:49
  • Sorry, aberkie, I just edited my example - I had an error in the first sample. The second loop was misleading! -> {% for data in row %} <td>{{ data }}</td> {% endfor %} is the right one – Max Feb 1 '15 at 15:53
1

Taken from carlcs' example, you'll probably want the clean "human readable" table headers. Looks like those aren't handed out in the template by default but you can get 'em with a trip to getFieldByHandle.

{% set field = craft.fields.getFieldByHandle('whateverField') %}

So something like this:

<table>
<tr>
<thead>
  {% set field = craft.fields.getFieldByHandle('whateverField') %}
  {% for column in field.settings.columns %}
    <th>{{ column.heading }}</th>
  {% endfor %}
</thead>

The just spit out the rest of the table like normal:

<tbody>
    {% for row in entry.whateverField %}
      <tr><td>{{ row.col1 }}</td><td>{{ row.col2 }}</td></tr>
    {% endfor %}
</tbody>        
</tr>   
</table>

If your table is within a matrix block—which complicates things—you need to do some additional finagling. The above link will show how to do that.

  • Thanks for your answer. Actually I was not looking for human readable headers, just a clean way to loop through table data, without manually typing the row.VALUE. So if my client created new tables, which I did not manually implement, they get output fine. I was looking for a solution, which does not involve a complex statement like the one Douglas McDonald mentioned ({% for key, data in row if not key|[0,2] == "col" %}), which seems to be impossible. – Max Feb 2 '15 at 7:29
0

Craft's Table Field Type probably isn't the right choice, if this is what you want to achieve:

So if my client created new tables, which I did not manually implement, they get output fine.

The Table Field Type isn't really meant to be created by users. Besides column names, the user also has to configure the field handle and add the field to the right Entry Type in the Settings section of the CP.

Then there's also the problem with dynamically accessing the field with an unknown handle from the template.

{% for row in entry.unknownTableFieldHandle %}
{% endfor %}

But there's this new plugin by Supercool: Table Maker. This should do exactly what you want (see the field's template syntax), plus it allows the user to define the table columns.

{% for row in entry.myTableFieldHandle.rows %}
    <tr>
        {% for cell in row %}
            <td>{{ cell }}<td>
        {% endfor %}
    </tr>
{% endfor %}

  • Just an aside here but I thought the same thing. A client is going to go through the trouble of setting up a new field so they can output it in a template somewhere that you originally didn't setup? Doubtful. That sounds like a recipe for disaster. The potential for breakage on a responsive website is huge if they would create too many columns, for example, and most tables need to be styled. That Tablemaker plugin seems interesting but what happens on mobile? You either need to hide columns, make CSS edits or do some funky scrolling so you an see the whole table. Percentages only go so far... – RitterKnight Feb 2 '15 at 17:09
  • 1
    @RitterKnight github.com/filamentgroup/tablesaw – carlcs Feb 2 '15 at 17:12
  • No doubt tablesaw is a great tool. You'd be surprised at how "crafty" clients are... I'd be afraid that using Table Maker a client would want to use it for layout purposes as well... I've seen it happen! – RitterKnight Feb 2 '15 at 18:17
  • Actually, this depends on your client. In this special case, it is possible and - talking of tables - a pretty usual case. It is in the nature of tables to contain a lot of different data with different columns. Sometimes you simply can't set up a different table for each of these. On mobile you can use different strategies for that, like the mentioned tablesaw, getbootstrap's responsive tables and so on. So no worries there. – Max Feb 5 '15 at 7:53

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.