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 06 · Rendering areas in templates

Images, files, records and collections

The getters for areas that point at something: an image, a file, one record or several, a component or a collection. What each returns when the layout area is empty, which ones run a query, and where the images and collections units take over.

Who this is for. The template developer rendering areas that hold media, references or components. Afterwards you can guard each one correctly when it is empty and keep record lookups out of loops.

Images

In a template, getAreaImage(name) always returns an image object: the image, or a blank image when the area is empty. Test it with isBlank(). The blank image returns harmless values from every method: an empty name, path and HTML, a width and height of 0, and getResized(), getTransformed(), toFormat() and toOptimizedFormat() return the blank image itself. A chain such as getAreaImage('hero')->getResized(800, 450)->getHtml() prints nothing on an empty area rather than failing.

getAreaImageOrPlaceholder(name, width, height) returns a generated placeholder of that size in place of the blank image, when the setting Files.images.usePlaceholder allows it. Resizing, formats and rendering are in Images in templates.

Files

getAreaFile(name) returns the file, or null when the area is empty, in a template as everywhere else. There is no blank file, so test it with a plain null check: calling isBlank() on null is a fatal error.

Template code
{$image = $component->getAreaImage('image')}
{if !$image->isBlank()}
  {$image->getResized(800, 450, 'fit')->getHtml(['alt' => $component->getAreaText('imageAlt')])}
{/if}
{$file = $component->getAreaFile('brochure')}
{if $file}<a href="{$file->getPath()}">Download</a>{/if}
{% set image = component.getAreaImage('image') %}
{% if not image.isBlank() %}
  {{ image.getResized(800, 450, 'fit').getHtml({'alt': component.getAreaText('imageAlt')}) }}
{% endif %}
{% set file = component.getAreaFile('brochure') %}
{% if file %}<a href="{{ file.getPath() }}">Download</a>{% endif %}

Records

getAreaRecord(name) returns the record a record area references, and getAreaRecords(name) a collection of the records a records area references, in the order the author stored them. Both are already converted to the models a template works with, so you call the same methods as on any other record. Both return null for an empty area, in a template too: there is no blank record.

Both look the records up with a query on every call, and the result is not kept between calls on the same content. Read the area once into a variable. A loop over a null result is safe in both engines and runs zero times; calling a method on the null, such as count(), is not.

Template code
{$related = $post->getAreaRecords('related')}
{if $related}
  <ul>
    {foreach $related as $item}<li>{$item->getName()|escape}</li>{/foreach}
  </ul>
{/if}
{% set related = post.getAreaRecords('related') %}
{% if related %}
  <ul>
    {% for item in related %}<li>{{ item.getName()|e }}</li>{% endfor %}
  </ul>
{% endif %}
One query per row

A reference area read inside a loop over many records, such as a related area on every row of a list page, runs one query per row. Do it only when rendering each row's references is the point of the list, and never call the same getter twice for one row.

Components and collections

  • getAreaComponent(name) returns the component, or a blank component when the area is empty. Test it with isBlank().
  • getAreaCollectionElements(name) returns a collection of the area's elements, and an empty collection when there are none. It never returns null, in a template, the admin or the API, so count it or loop over it without a null check.

Each element of a collection gives its component with getComponent(); in a template it falls back to the element's global component. An element's isEmpty() is true only when it holds neither a local nor a global component. Looping over the elements, nesting collections and rendering each component are in The collection loop and rendering a component.

An older site may still have widget areas. Widgets are deprecated: a component's template reads records with the query helper $_query instead, as Page and component layouts shows.

Template code
{$items = $component->getAreaCollectionElements('items')}
{if $items->count() > 0}
  <div class="grid grid--{$items->count()}">
    {* render each element as the collection loop page shows *}
  </div>
{/if}
{$banner = $component->getAreaComponent('banner')}
{if !$banner->isBlank()}
  {* render the banner component *}
{/if}
{% set items = component.getAreaCollectionElements('items') %}
{% if items.count() > 0 %}
  <div class="grid grid--{{ items.count() }}">
    {# render each element as the collection loop page shows #}
  </div>
{% endif %}
{% set banner = component.getAreaComponent('banner') %}
{% if not banner.isBlank() %}
  {# render the banner component #}
{% endif %}
Go deeper

Was this page helpful?

Your answer goes to the docs team.