Mastering MediaWiki Cargo Extension for Structured Data

The working Cargo pattern: declare tables, store from templates, query with #cargo_query — types, formats, `where` clauses and performance habits.

Cargo gives MediaWiki database-style tables with querying, stored and managed entirely from wiki pages. It is the straightforward choice for records — inventories, items, people — where Semantic MediaWiki would be overkill. This is the working pattern: declare, store, query, with the details the quickstarts skip. (Installation is one Composer line; the extension page covers it.)

1. Declare the table

A template page carries the declaration — #cargo_declare runs when the template is saved and (re)creates the table:

Template:Books
<includeonly>{{#cargo_declare:_table=Books
 |Title=String
 |Author=String
 |PublicationYear=Date
 |Pages=Integer
 |Cover=File
}}{{#cargo_store:_table=Books
 |Title={{{title|{{PAGENAME}}}}}
 |Author={{{author|}}}
 |PublicationYear={{{year|}}}
 |Pages={{{pages|}}}
 |Cover={{{cover|}}}
}}</includeonly>

Field types include String, Integer, Float, Date, Datetime, Boolean, Page, File, Coordinates, Wikitext, Wikitext string, Searchtext, URL and Email. #cargo_store pushes the values into the table on every save. Field names keep type consistency: changing an Integer to String requires re-running the maintenance script for table alteration.

2. Query with #cargo_query

{{#cargo_query:
 |tables=Books
 |fields=Title,Author,PublicationYear,Pages
 |where=PublicationYear>=2000 AND Pages>300
 |order by=PublicationYear DESC
 |format=table
 |limit=20
}}

The SQL-flavored where accepts =, !=, >=, LIKE, IN (...), HOLDS (list fields) and boolean combinators. Key parameters:

  • tables= — multiple tables with join conditions: tables=Books,Reviews|Books._ID=Reviews.Book
  • group by=, having= — aggregates (fields=COUNT(*),SUM(Pages))
  • format=table|list|template|count|dynamic table|timeline|calendar — output shape; template renders rows through a template
  • default= — message when the query finds nothing

3. Data entry: forms and templates

Cargo tables are populated through the templates that contain #cargo_store. Pair it with Page Forms so editors never touch the store call: a form with fields bound to the template parameters (autocomplete on Author keeps names consistent). Page Forms' field types map onto Cargo types, and validation happens at the form level (e.g. input type=date for Date fields).

4. Presentation and navigation

  • format=dynamic table — client-side filterable/sortable table from a small result set
  • format=template with intro=/outro= — full layout control (cards, grids)
  • format=count — dashboard numbers
  • Geodata: Coordinates fields + format=map (Leaflet/OpenLayers) for location records

5. Performance habits

  • Index what you filter — Cargo creates an index on the template's declared fields; heavy where/order by columns beyond that can be indexed via the extension's maintenance functions — but usually, restraint in field count keeps tables fast
  • Limit the rowslimit= on every user-facing query; full-table scans in #cargo_query are a page-load killer
  • Store what you query — avoid query-time computation; prestore derived columns (e.g. normalized dates) at #cargo_store time
  • Delete discipline — deleting a page does not delete its Cargo rows automatically unless the deletion hook runs; re-run the relevant maintenance for orphaned rows if records linger

6. The pattern in practice

One template per record type (declaration + store), forms for entry, list pages with #cargo_query, and templates for rich output. Cargo's strength is that the whole data layer lives on the wiki: schema, data and reports are all pages, versionable like everything else. The extension documentation documents every field type and parameter.

Subscribe to MediaWiki Tips and Tricks

Don’t miss out on the latest articles. Sign up now to get access to the library of members-only articles.
jamie@example.com
Subscribe