Everything a developer changes on a Core dna site lives in its theme repository. This guide covers the repository and its build, printing records in templates, stylesheets and client-side code, and the parts of a site around the main content.
L1–L2
Unit 02 · Records in templates
Custom fields in templates
Records in templatesPage 14 of 35Updated
Menus, products, users, categories and a dozen other record types carry custom fields an administrator defines. Which records have them, the getters by type, what comes back when nothing is stored, and why a misspelt field name is not an error in either engine.
Who this is for. The theme developer printing an author-defined value: a menu item's icon, a product's badge, a category's colour. Afterwards you can read any custom field, handle the empty case on purpose, and tell an unset field from a misspelt one. Whether a value should be a custom field at all is Custom fields in Guide 5.
Which records have them
A custom field is defined once, in the admin, for a whole record type. In templates these record types have them: books, book units and book pages; menus and menu elements; categories, tags, layouts and SEO settings; products and product categories; forms; invoices; addresses; authors, people and users.
Pages, blog posts and custom entity records do not. Their extra values are layout areas, read with the area getters: Area getters, by return type.
Custom field values belong to the record, not to a revision, so a change to one shows on the next uncached render with nothing to publish.
The getters
Getter
Returns
getCustomFieldText(name, default)
The stored value as text: any text field, a select's key, a date as written
getCustomFieldBoolean(name, default)
true only when the stored value is '1', as a switch stores it
Whether the record type defines a field of that name, whatever is stored
A real one: this documentation site's header menu gives each menu element a text field navKey, which the navigation template reads to highlight the current section.
{% set menu = _menu.get('Header Menu') %}
{% if menu %}
{% for element in menu.getRootElements() %}
{% set key = element.getCustomFieldText('navKey', '') %}
{% set isNew = element.getCustomFieldBoolean('isNew', false) %}
<a href="{{ element.getUrl() }}" data-nav="{{ key|e('html_attr') }}">
{{ element.getName()|e }}{% if isNew %} <span class="badge">New</span>{% endif %}
</a>
{% endfor %}
{% endif %}
What comes back when nothing is stored
getCustomFieldText() and getCustomFieldBoolean() resolve in this order:
the stored value, when the record has one, even if it is an empty string;
the field's default value, when the field has one;
the default you pass as the second argument;
nothing (null), when you passed none.
Pass a default whenever the template needs a value to work with, and test for the empty string as well as for nothing when a stored value can be blank.
A misspelt name is not an error
A name the record type does not define behaves exactly like a field with nothing stored: you get your default, or nothing, in both engines. A typo produces an empty badge and no message. When a value is unexpectedly empty, ask the record whether the field exists:
Template code
{if !$element->hasCustomField('navKey')}
<!-- navKey is not defined for menu elements on this site -->
{/if}
{% if not element.hasCustomField('navKey') %}
<!-- navKey is not defined for menu elements on this site -->
{% endif %}
Remove the check once you have your answer. Field names are case-sensitive, and they are site data: a field created on the development site does not exist on production until someone creates it there too.