The platform's central abstraction, in one line: an input type defines what an editor can enter, a layout area binds one input type to a named slot, a layout groups areas and points at a template, a record or component holds the content for each area, and the template reads it back. Read this book straight after the template languages.
L1–L3
Unit 09 · Patterns, live preview and upkeep
A component gallery for authors
Patterns, live preview and upkeepPage 45 of 45Updated
Build a page that lists every component layout with its thumbnail, purpose and areas, read from the layout records themselves so that it cannot drift from what authors can choose, and keep its thumbnails honest.
Who this is for. Developers handing a component library to authors. After this page you can build a gallery page that shows authors every component they can add and what each one asks for, and that stays current without anyone editing it.
Why read it from the layouts
A hand-written catalogue goes stale the first time someone adds a component. A gallery that reads the layout records lists what the component picker offers, by the picker's own rule: a component layout is a layout with no controller, and the picker shows those that are not hidden. A new component appears in the gallery when its layout is created, and a hidden one drops out.
The gallery template
The query helper $_query can read layouts. The template below lists the component layouts by name, each with its thumbnail and its areas: name, label, input type, and whether the area is an attribute. Put it in the template of a page layout used only by the gallery page. It also keeps only templates under components/, a common convention; change that test to match your repository.
{% for layout in _query.findBy('Common\\Entities\\Layout', {}, {'name': 'ASC'}, 500) %}
{% if layout.getController() is null and not layout.isHidden() and layout.getTemplate() starts with 'components/' %}
<section>
<h2>{{ layout.getName()|e }}</h2>
{% if layout.getImage() %}{{ layout.getImage().getResized(480, 300, 'fit').getHtml({'alt': ''}) }}{% endif %}
<ul>
{% for area in layout.getAreas() %}
<li><code>{{ area.getName() }}</code> {{ area.getLabel()|e }}, {{ area.getType().getName()|e }}{% if area.isAttribute() %} (attribute){% endif %}</li>
{% endfor %}
</ul>
</section>
{% endif %}
{% endfor %}
Saying what each component is for
Authors need one sentence per component: what it is for and when to choose it. The layout record has a description field, read with $layout->getText(), but the layout form in the admin has no input for it, so on a layout created in the admin it is empty. The call is safe; it returns nothing. Two ways round it:
A custom field on layouts. The layout form shows custom fields, so a field added for the sentence can be filled in the admin like any other.
The API. The description field can be written through the API, which does not depend on the form.
Either way, print the sentence under the layout's name in the gallery.
Keeping thumbnails honest
A layout's image is its thumbnail, set on the Organisation tab of the layout form and stored in the folder layout-preview. The same image is the component's tile in the component picker, so keeping it current matters twice over: in the gallery, and when authors choose a component. A layout with no thumbnail gets a generic icon in the picker.
Take each thumbnail from a real page with realistic content, at the same size and crop across the library.
Retake it whenever a change to the component's template or CSS changes how it looks. Make the retake part of finishing any change to a component.
In the quarterly audit, compare each thumbnail with the live component.
Keeping it for authors
The gallery lists every component layout, area names included. It is a working page for authors and developers rather than for visitors: restrict it to signed-in users and leave it out of the sitemap. It is also a ready-made page for the in-place editing test on Live preview and testing in-place editing.