Guide 04 · Developer docs

Input types, layouts, components and images

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 07 · The shorthand, and working with each input type

Files, records and component areas

Rendering a download link with the file's name and size, and why files are public; linking to a record and reading a set of records once; what the shorthand gives for component and collection areas; and where images are covered.

Who this is for. Developers rendering areas that hold an object rather than text. After this page you can build a download link from real file metadata, link records correctly, read a set of records once, and render blocks with or without the shorthand.

Files

A file area holds a file from the file manager. The shorthand and getAreaFile() return the file object, or null when the area is empty, so test it first. Printed as it is, the file gives its URL, not its name.

The file object has no name or size of its own. The pieces are one step away:

You wantRead
The URLgetPath(), or its alias getFullPath()
The file namegetFilename()->getName()
The size, in bytesgetActiveRevision()->getVirtualFile()->getSize()
The MIME typegetActiveRevision()->getVirtualFile()->getType()
A rough group name, not a MIME typegetType()
Template code
{$file = $component->getAreaFile('download')}
{if $file}
    {$size = $file->getActiveRevision()->getVirtualFile()->getSize()}
    <a href="{$file->getPath()}" download>
        {$file->getFilename()->getName()|escape} ({ceil($size / 1024)} KB)
    </a>
{/if}
{% set file = component.getAreaFile('download') %}
{% if file %}
    {% set size = file.activeRevision.virtualFile.size %}
    <a href="{{ file.path }}" download>
        {{ file.filename.name|e }} ({{ (size / 1024)|round(0, 'ceil') }} KB)
    </a>
{% endif %}

getType() on the file currently reports .docx and .xlsx files as file, its catch-all group, so do not choose an icon from it for those. The MIME type on the active revision's virtual file is the reliable value.

Files are public

Files in the file manager are public by design: there is no publish switch and no per-file permission, and anyone with the URL can open the file. Never use a file area as private storage. To limit a download to members, gate the page or the part of it that shows the link; Members-only files explains how.

Record and records

A record area links to one other record, such as a page or a product; a records area links to several, in the order the author arranged them.

  • The shorthand and getAreaRecord() return the record, or null.
  • The shorthand and getAreaRecords() return a collection of records, or null when the area is empty: never an empty collection. Test it before looping.

Link to a record with its own getFullPath(), as the shorthand fullPath, rather than building a URL from its slug. On a page fullPath and name are real getters, so the shorthand returns the record's own values, which is what you want here and the reason an area should never take those names.

Template code
{$targetPage = $component->getAreaRecord('targetPage')}
{if !empty($targetPage)}
    <a href="{$targetPage->fullPath}">{$targetPage->name}</a>
{/if}
{% set targetPage = component.getAreaRecord('targetPage') %}
{% if targetPage is not empty %}
    <a href="{{ targetPage.fullPath }}">{{ targetPage.name }}</a>
{% endif %}

Read a set once

Assign getAreaRecords() to a variable, then test the variable and loop over it, rather than calling the getter once for the test and again for the loop. The getter loads the linked records together in one call, not one per element, so a single call serves the whole list:

Template code
{$pages = $component->getAreaRecords('pages')}
{if $pages}
    <ul class="related">
    {foreach $pages as $item}
        <li><a href="{$item->fullPath}">{$item->name}</a></li>
    {/foreach}
    </ul>
{/if}
{% set pages = component.getAreaRecords('pages') %}
{% if pages %}
    <ul class="related">
    {% for item in pages %}
        <li><a href="{{ item.fullPath }}">{{ item.name }}</a></li>
    {% endfor %}
    </ul>
{% endif %}

Component and collection areas

For a component area the shorthand renders the component; for a collection area it renders the collection's elements in order. Either way you get the rendered HTML, the same as getAreaHtml(), not an object you can inspect:

Template code
<div class="sections">{$page->pageSections}</div>
<div class="sections">{{ page.pageSections }}</div>

Use the explicit accessors when you need the object: to test whether a component area is filled, or to wrap, count or pass something to each element. getAreaComponent() returns the component and never null: an empty area gives a blank component, which prints as an empty string and answers isBlank() with true. Test it when the surrounding markup should disappear too:

Template code
{$promo = $component->getAreaComponent('promo')}
{if !$promo->isBlank()}
    <aside class="promo">{$promo->getHtml()}</aside>
{/if}
{% set promo = component.getAreaComponent('promo') %}
{% if not promo.isBlank() %}
    <aside class="promo">{{ promo.getHtml() }}</aside>
{% endif %}

For a collection, walk its elements and render each one with getHtml(). The loop, written once for both engines and covering local and global components alike, is on The collection loop and rendering a component. One rule from there matters whenever you pass variables to getHtml(): give them names of their own. A name the page already provides, such as page, keeps the page's own value, and a variable named component replaces the component being rendered.

Older templates may still have widget areas. Widgets are deprecated: a block that lists records is now a component that queries them, as Computed blocks with the query helper shows.

Images

Image areas are the most used area type and have their own unit, next. Images in templates covers the image object and printing it; Image URL parameters covers what an image URL can ask for.

Go deeper

Was this page helpful?

Your answer goes to the docs team.