Skip to content

Tables

A repeating structure inside an object: slides in a slider, product specs, team members. A table is described once, then attached to a block through a Table field — and the editor fills in its rows from the block's own form.

PageBlocks → Constructor → Tables.

The table constructor

SettingWhat it does
Table nameHow the table is labelled when picking one. Required
ModelWhere the rows live — see below. Required
PaginationBreak the list into pages. On by default
SearchA search box above the list — and the same box makes the table a source of the global search
ModerationNew rows wait for review — see Moderation. The box is absent while the pageblocks_moderation setting is off

Tabs: Fields, Tabs, Columns, Search, Filtering, Actions and API.

The split is worth getting straight early: fields are what an editor fills in, columns are what they see in the list. They are separate sets and need not match — a field can be searchable or filterable without having a column at all.

No separate database table is created

Rows from every table live in one pb_table_data, with the values as JSON in the data column. Ownership is three references: model_type / model_id for the owning block, constructor_id for the table, field_id for the field.

What follows from that

A table costs nothing to create: no migration, no schema change. The flip side is that searching by a "column" of such a table means JSON_EXTRACT — there is no ordinary index on it. For dozens of rows per block that is irrelevant; for tens of thousands it is a reason to consider a model of your own.

Columns

SettingWhat it does
Column typeA field, a group of fields, or the first non-empty of several
FieldWhich field to show
CaptionOverrides the field's label
WidthIn pixels, up to 600. 0 means fit automatically
RendererHow to present the value
Default valueWhat to show when the value is empty
SortableAllow sorting by this column

Renderers: text (long text is truncated), html, image, video, date, boolean, button, color. Columns holding an icon or a status get their renderer automatically.

The group type packs several fields into one cell; first non-empty takes whichever of the listed fields has a value. Both exist for the case of many columns and little room.

Output in a template

The value of a table field is an array of rows:

html
<div class="slides">
    {foreach $slides as $slide}
        <div class="slide">
            <h3>{$slide.title}</h3>
            <img loading="lazy"
                 src="{$slide.image.url}"
                 width="{$slide.image.width}"
                 height="{$slide.image.height}"
                 alt="{$slide.image.title}">
        </div>
    {/foreach}
</div>

Check an optional field before printing it:

html
{if $description}
    <p>{$description}</p>
{/if}

Not every row reaches the template

Only published ones: the publish date is set and not in the future, and the row is not deleted. The order is the one set by dragging. A row that is "missing from the site" is usually just unpublished.

A block with a table on the page

Model: an ordinary table, or a resource

Model in the settings picks the storage.

  • PbTableData — the above: the row lives in shared storage and exists inside its block.
  • PbResource — the row goes to pb_resources and gains resource columns of its own: pagetitle, alias, uri, template, content, menuindex, hidemenu and the rest. Such a row can have its own address and its own template.

The second is for when a row is really a page: a product card, a catalogue article, a directory entry. Ordinary MODX resources are untouched — pb_resources is its own table and does not replace site_content.

A list like that can be filtered and sorted as a full catalogue:

A table with filters

© PageBlocks 2019-present