Skip to content

Intro ​

PageBlocks registers seven MODX snippets on install. They exist so the component is reachable from an ordinary MODX template: you paste a call, you get output. No controller, no route, no migration to our template engine.

SnippetWhat it does
pbListUniversal lister with built-in pagination over any model
pbBlocksRenders the constructor blocks of a resource
pbResourcesFlat list of MODX resources
pbUsersFlat list of users
pbMenuResource tree for navigation
pbFiltersReady-made filter form for a constructor table
pbJsonRenders a JSON value through an inline template

Calling them ​

Both spellings work. In a MODX template:

[[!pbList? &model=`PbResource` &parent=`[[*id]]` &tpl=`myItem` &limit=`10`]]

In a Fenom template:

php
{'!pbList' | snippet: ['model' => 'PbResource', 'tpl' => 'myItem', 'limit' => 10]}

Keep the ! — these snippets are meant to be uncached, otherwise MODX will freeze the first render into the page cache.

Array parameters are JSON ​

A MODX snippet receives every parameter as a string, so anything array-shaped is written as JSON and decoded by the snippet:

[[!pbList?
    &model=`PbResource`
    &tpl=`myItem`
    &where=`{"template":4}`
    &orderBy=`{"publishedon":"desc"}`
]]

Invalid JSON is passed through untouched, so a plain 12,7,3 id list stays an id list.

Shared output parameters ​

pbResources, pbUsers, pbMenu and pbBlocks share one output contract — the same one pdoTools users already know:

ParameterDefaultDescription
tpla default chunk of the componentChunk rendering a single row. Without it the snippet falls back to its default chunk, so a bare [[!pbResources]] already prints something.
tplWrapperChunk wrapping the whole output. Receives output and total.
tplEmptyChunk rendered instead of the output when nothing was found.
outputSeparator\nGlue between rendered rows.
toPlaceholderPut the output into a MODX placeholder and return nothing, so the call can sit at the top of a template and print further down.
showLogPrint what the snippet complained about next to its output. Shown to an authenticated manager only.

Inside tpl the row is available as item, and its 1-based position as idx:

php
<div class="card">
    <span class="num">{$idx}</span>
    <a href="{$item->uri}">{$item->pagetitle}</a>
</div>

Which chunk does tpl mean? ​

One rule for every snippet, and the same one the rest of PageBlocks uses — the source is part of the value, not a separate parameter:

&tpl=`myItem`                  → MODX chunk named myItem
&tpl=`file:chunks/myItem`      → core/App/elements/chunks/myItem.tpl
&tpl=`file:pbShop:chunks/card` → file from a named source
&tpl=`@INLINE <b>{$item->pagetitle}</b>`  → template spelled inline

.tpl is appended when you leave it off. There is no fallback between the two: a file: that does not exist is an error in the log, not a quiet look-up in the database — so a typo never silently resolves to something else.

chunk: is deprecated

chunk: used to mean "the database" in pbList, while a bare name there meant a file — the opposite of pbResources, pbUsers and pbMenu, where a bare name has always been the database and chunk: rendered nothing at all. A bare name is now the database everywhere. chunk: still works for one major version and writes a deprecation line into the MODX log.

Zero step ​

Each of the list snippets ships a default chunk, installed as an ordinary chunk you can find in the element tree, edit in place, or copy next to it:

SnippetDefault chunk
pbList, pbResourcespageblocks.resource
pbUserspageblocks.user
pbMenupageblocks.menu + pageblocks.menu.wrapper
pbBlockspageblocks.blocks.wrapper — the .pb-page wrapper the design tokens are declared on; it has no default row chunk, each block renders through the chunk the constructor gave it

So [[!pbResources]] renders a list before you have written a single chunk. Pass &tpl and yours wins.

What an upgrade does to a default you have edited ​

The installer stores the sha1 of the shipped content of every default chunk, so an upgrade can tell your work from ours:

State of the recordWhat the upgrade does
untouched (hash matches what we shipped)overwritten, so fixes to the default actually reach you
edited by youleft alone, with a line in the MODX log naming the chunk and where the shipped original is
marked staticnever touched, whatever the hash — the content of a static element is a file, and it is yours

Upgrading from a version that did not store hashes yet? Then an edit is indistinguishable from a changed default, and the safe side wins: your version is kept.

static points at core/App, not at the component

Tick "static" on a default chunk and its content moves to core/App/elements/chunks/<name>.tpl — the site-owned tree, which an upgrade only ever adds to. Pointing it inside core/components/pageblocks/ would mean the file resolver overwrites your content on every upgrade while formally "not touching the record".

Need the rows as data rather than HTML? That is what the helpers are for — resources(), users() — and they are the Fenom-side entry point anyway.

Where a snippet's code lives ​

The seven snippets are installed as static elements: the code that runs is the file under core/components/pageblocks/elements/snippets/, and the database record only registers the element. An upgrade replaces the file; the record is left alone.

Editing a snippet in the manager writes into the component

Because the element is static, saving it in the manager writes your changes into the component's own file — and the next upgrade overwrites that file. A snippet is component code: it is not meant to be edited, it is meant to be updated. Need different behavior? Copy the file next to it under your own name and register your own snippet — chunks are the part meant for your edits.

A file change needs a cache refresh to take effect: MODX compiles elements into core/cache/includes, and until the cache is cleared the compiled copy is what runs. So after a deploy that touched these files, clear the MODX cache — from the manager, or with $modx->getCacheManager()->refresh() from your own deploy script.

Why is it empty? &showLog ​

A snippet that renders nothing is the hardest thing to debug in MODX: the tag disappears from the page and the reason stays in the MODX log, where nobody looks while editing a template. So add &showLog=`1` and the snippet prints what it collected:

[[!pbList? &tpl=`crad` &showLog=`1`]]
html
<pre class="pb-log">[Fenom] chunk 'crad' not found in the database; for a file write file:crad</pre>

Other things it will tell you, instead of rendering nothing at all:

MessageWhat happened
chunk 'x' not found in the database; for a file write file:xa typo in tpl, or the chunk lives in a file
file chunks/x.tpl not found under …file: points at a path that does not exist
unknown model 'X'&model does not resolve under PageBlocks\App\Models\
&table is requiredpbFilters without a table
table 'X' has no fields marked filterablethe table is there, the filter flags are not
nothing found, the query returned no rowsthe chunk is fine — the where/scopes matched nothing

The block is shown only to a user logged into the manager, so &showLog can be left in a template while a page is being built: a visitor sees the output alone. Every message except "nothing found" also goes to the MODX error log, with the file and line it came from.

© PageBlocks 2019-present