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

The area getters and what each costs

One set of getters reads areas on every record that has them: pages, book pages, blog posts, product descriptions, custom entity records, global components and components. Every getter, what it returns when an area is filled, empty or missing, and what each call costs.

Who this is for. The template developer reading layout areas in Smarty or Twig. Afterwards you can pick the getter that fits an area, predict what it returns when the author left the area empty, and keep the expensive calls out of loops.

One surface, every container

The area getters are defined once, in a trait shared by every record that holds areas. The same calls work on a page, a book, unit or book page, a blog post, an FAQ question, a product or product category description, a custom entity record, a global component and a component. Learn them once: nothing on this page is specific to one content type.

The containers come in two kinds. Every one in that list except a component is versioned: its content lives in revisions, and a getter called on the record reads the record's active revision. A component has no revisions of its own: its areas sit on the component, which is stored with the revision whose area holds it. So the first two lines below are the same call, and the shorthand on the last line, the normal way to print a value, is the shortest way to it:

Template code
{* on a page, a book page or any other versioned record *}
{$page->getAreaText('intro')}
{* the same call, made on the active revision *}
{$page->getActiveRevision()->getAreaText('intro')}
{* the shorthand, the shortest way to it *}
{$page->intro}
{# on a page, a book page or any other versioned record #}
{{ page.getAreaText('intro') }}
{# the same call, made on the active revision #}
{{ page.getActiveRevision().getAreaText('intro') }}
{# the shorthand, the shortest way to it #}
{{ page.intro }}

There is one exception. getAreaContent() is not passed through to the active revision: on a page, a blog post or any other versioned record, call it on the revision, $page->getActiveRevision()->getAreaContent('name'). On a component, call it directly. getAreaContentOrDefault() is passed through like every other getter.

Every getter

Each getter takes the area's name as the layout defines it. The getters are the explicit form of the shorthand, {$page->intro} in Smarty and {{ page.intro }} in Twig, which is how a template normally prints a value (The area shorthand). Call a getter when you need an argument or a method on the result, when the value goes into an HTML attribute, a script or JSON, or when the area's name is also a getter of the record, such as name or url. The table gives what each returns in a template. It leaves out getAreaWidget(): widgets are deprecated, and a component's template reads records with the query helper $_query instead. Where it says blank, the getter returns a blank object, a stand-in that prints as an empty string and answers true to isBlank(); the same call made in the admin or through the API returns null instead.

GetterFilled areaEmpty areaName the layout does not have
getAreaType(name)The input type recordnullnull
getAreaCategory(name)The category, a stringnullnull
getAreaEditor(name)The editor, a stringnullnull
getAreaText(name)The stored value, a stringnullnull
getAreaHtml(name)The rendered value''''
getAreaValues(name)An array of option keys (multiselect only)[][]
getAreaDate(name)A \DateTime (date and datetime editors)nullnull
getAreaImage(name)The imageBlank imageBlank image
getAreaFile(name)The filenullnull
getAreaRecord(name)The referenced recordnullnull
getAreaRecords(name)A collection of the referenced recordsnullnull
getAreaComponent(name)The componentBlank componentBlank component
getAreaCollectionElements(name)A collection of the elementsAn empty collectionAn empty collection
getAreaContent(name)The stored content object (not on a versioned record, above)Blank contentBlank content
getAreaDefaultContent(name)The area's default contentBlank contentnull
getAreaContentOrDefault(name)The stored content, or the default when nothing is storedBlank contentBlank content
getAreaAttributesContent(name), getAreaAttributesDefaultContent(name), getAreaAttributesContentOrDefault(name)An array of content objects keyed by attribute nameThe same; [] when the area has no attributes[]
hasAreaWithName(name)truetruefalse

Three things follow from the table.

  • A missing name is not an error. Every getter answers a name the layout does not have as it answers an empty area, so a misspelt name looks like an empty area. hasAreaWithName() is the one call that tells them apart.
  • Blank objects only for images, components and raw content. Files, records, text, values and dates give null or [] in a template too; there is no blank file. A collection gives an empty collection, never null, everywhere.
  • Two getters refuse the wrong kind of area. getAreaText() on an area outside the text category returns the string Error: cannot get text content for area of type …, with the input type's name and category, and the template prints it like any value. getAreaDate() on a filled area whose editor is neither date nor datetime throws: "Could not get date from area … as it is not a date / datetime type".

The test that fits each getter, and the one check that covers every category, are in Testing whether an area is empty.

What each call costs

An area's stored content arrives with the record. Some getters read only that; others load or look up what the area points at. Four tiers, cheapest first:

TierGettersWhat happens
FreehasAreaWithName(), getAreaType(), getAreaCategory(), getAreaEditor()Reads the area list already in memory; no content is touched.
CheapgetAreaText(), getAreaValues(), getAreaDate(), getAreaContent(), getAreaDefaultContent(), getAreaContentOrDefault(), the attribute accessorsReads the stored value from content loaded with the record. getAreaDate() parses a new date object on every call; an attribute accessor makes one such read per attribute.
One loadgetAreaImage(), getAreaFile(), getAreaComponent(), getAreaCollectionElements()The first call can load what the area points at. The content keeps it, so a second call on the same content does not load it again.
A query every callgetAreaRecord(), getAreaRecords(), and getAreaHtml() or the shorthand on a record, records, component or collection areaLooks the referenced records up on every call, with nothing kept between calls. getAreaHtml() also renders the record's or component's template.
A reference read inside a loop

Each getAreaRecord() or getAreaRecords() call runs its own query, even on the same content. A list page that reads a related area for every row runs one query per row: the classic query-per-row shape.

The rule: free and cheap calls can go anywhere, loops included. Call a one-load getter once per render and keep the result in a variable. Keep the last tier out of loops: read a value once above the loop when every row needs the same one, and read a reference on every row only when rendering each row's referenced content is what the loop is for.

Go deeper

Was this page helpful?

Your answer goes to the docs team.