Element-query tables (Pro)

The thing a WordPress table plugin cannot do, because WordPress has no element query. Point a table at your content and it keeps itself current.

Defining one

On a table's edit screen, set Source to An element query, then:

FieldWhat goes in it
Element typeEntries, categories, assets, users, Commerce products — anything registered as an element type
CriteriaQuery parameters as JSON: {"section": "products", "orderBy": "title asc", "limit": 200}
ColumnsOne row per column: a heading, what it reads, and the value
Include a header rowWhether the column headings become row 1

A column reads one of three things:

  • An attributetitle, postDate, url
  • A custom field — the field's handle
  • An object template{{ element.url }}, or any Twig you like. Both element and Craft's own object are available.

Values are stringified sensibly on the way out: dates are formatted, elements become their labels, and a list of related elements becomes a comma-separated list.

Materialised, not live

The grid is built and stored, not resolved when the page renders. A table on a busy page must not run an element query per visitor, and a query table has to behave exactly like a hand-typed one — same caching, same export, same embed.

So freshness is kept by rebuilding:

  • Saving an element of a matching type queues a rebuild on a 30-second debounce, so a resave of 400 entries costs one rebuild rather than four hundred.
  • Refresh now on the table's edit screen rebuilds immediately.
  • php craft legs/tables/refresh rebuilds every query table — the cron option if you would rather not rebuild on save at all. Turn refreshOnElementSave off in that case.

The matching is deliberately coarse: any element of the configured type schedules a rebuild, rather than Legs trying to work out whether this element would have matched that criteria. Getting that wrong in the strict direction means a table that quietly stops updating, which is far worse than an occasional unnecessary rebuild of a 500-row query.

Safety rails

Criteria are filtered against a list of parameters a table has any business setting — Craft::configure() would happily set any public property on the query, and a stored config is not a place to accept arbitrary ones. limit is always applied, defaulting to 500 and capped at 5,000: a query table with no limit is a table that can grow to 40,000 rows without anyone deciding it should.

On a multi-site install

A translated query table runs its query per site — the entries in a Spanish section are not the entries in an English one — and each site's grid is built and saved on its own. A shared one is built once and copied everywhere. See multi-site.