Using Custom Fields

Custom fields let you store extra information on Oncord records, such as contact preferences, product features or answers collected during event registration. Once a field is created, you can edit its values through the dashboard, collect them through forms, and use them in page templates and repeaters.

In this tutorial

Common uses for custom fields

Store extra contact information

Add information that your organisation needs alongside Oncord's standard contact details. Examples include a membership number, industry, preferred location, interests or an account reference from another system.

Choose the Contacts component when the information belongs to the person across their interactions with your organisation. For example, contact_membership_number could store an identifier and contact_interests could store several selected interests. Use a text field for identifiers that may contain letters or leading zeroes.

Contact fields can be maintained in the dashboard or collected through a contact form. For the basic dashboard workflow, see Managing Contact Custom Fields.

Describe product features

Add specifications such as material, finish, care instructions, warranty information or included features to the Products component. For example, a text field named product_material might contain Recycled cotton, while a Check Boxes field named product_features might contain Machine washable and Lightweight.

These values can appear in a product's details or in a list of products. The repeater examples below show how to add them to a product card. A descriptive custom field does not automatically create purchasable options or track stock for variations; use the appropriate product configuration for those behaviours.

Collect event registration answers

Use the Event Attendees component for information that belongs to a particular registration, such as dietary requirements, accessibility requests, session choices or a question for the speaker. For example, event_attendee_dietary_requirements stores the answer on an attendee record for that event.

Use the Events component for information about the event itself, such as event_topic or an event-specific schedule link. A Topic selected by the event organiser describes the event; a dietary answer submitted by a registrant describes their attendance. Keeping those fields on the right component makes their values available in the right forms and templates.

Create a custom field

  1. Open Settings > Custom Fields in your website's dashboard (/admin/settings/customfields/) and click New Field.
  2. Choose the Component that owns the information: Contacts, Products, Events or Event Attendees, for example. The selector may show the full component class name.
  3. Enter a clear Label and check the Field Name. The form supplies the component prefix. Use the complete saved name, including that prefix, in your template code.
  4. Choose a Field Type. Use text for a short answer, Check Boxes for multiple selections, Combo Box or Radio Buttons for one choice, and Yes / No for a boolean answer. Enter the options when the selected type requires them.
  5. Configure relevant options, such as Mandatory, Show in Lists or the component's public visibility setting, then save the field.
  6. Open a record of that component, enter a value, save it and check the intended display or form.

The field names in this tutorial are examples, not built-in fields. Create them on your website or replace them with your own saved field names before copying the code.

Information belongs to Component Example field name
A contact \Components\Customers\Contacts contact_membership_number
A product \Components\Commerce\Products product_material
An event \Components\Marketing\Events event_topic
An event registration \Components\Marketing\Events\Attendees event_attendee_dietary_requirements

A field's definition stores its label, type and settings. Its value belongs to each individual record. The saved Field Name is field_id, such as product_material; custom_field_id is the separate numeric ID of the definition. Use the field name when reading a value from a record.

Collect and edit values

Dashboard records and contact forms

After creating the field, enter values on the relevant contact, product, event or attendee record. For a custom form, the input's datacolumn must match the saved field name, and the surrounding form must be configured to save the appropriate component.

You can include an existing field's generated input markup with CustomFields::getForm(). This example belongs inside a form already configured to save contact data:

    <!-- Place inside a form configured to save contact data. --> <logic:parse text="[?
    \Components\Settings\CustomFields::getForm('\Components\Customers\Contacts', 'contact_industry') ?]" />

getForm() returns an input control, not a saved value. The surrounding form supplies record binding, submission handling and save behaviour. See the forms tutorials for form setup. Built-in field types generate their input markup; the Custom UI type is available when you need to supply your own.

Event attendee registration

  1. Create the field on \Components\Marketing\Events\Attendees.
  2. Enable Show on Public RSVP Form on the field if attendees should fill it in themselves.
  3. Open the event and its RSVP Options tab.
  4. Select the field under Registration Fields. If guests should answer it too, configure Guest Registration Fields as appropriate.
  5. Save the event and check its public RSVP form.

Creating the attendee field makes it available for event configuration; selecting it for an event determines where it is used. Public visibility and selection are separate settings. See Events Management for the wider registration workflow.

Use custom fields in repeaters

A repeater gives you one record at a time through its as variable. Custom field values are available on that record alongside the standard fields. You do not need Display Code to use them in a template.

Show a text field on each product

Create a text field named product_material on Products and enter values on a few products. This example lists up to six online products, with a Material line only where a value is present:

    <data:repeater     datasource="\Components\Commerce\Products::getAll()"     datafilter="[?
    [['product_is_offline', '=', 0]] ?]"     datalimit="6"     as="product">     <article
    class="product-summary">         <h3>[? $product['product_title'] ?]</h3>         <logic:if
    test="$product['product_material'] !== null && $product['product_material'] !== ''">
    <p>Material: [? $product['product_material'] ?]</p>
        </logic:if>
    </article>
</data:repeater>

as="product" makes the current record available as $product. Inside that repeater, $product['product_title'] reads the standard title and $product['product_material'] reads the custom field. The conditional keeps the label and value together, and [? ... ?] HTML-encodes the output.

The datasource attribute takes a bare expression; the datafilter attribute in this example contains an evaluated array. If you already have a product repeater, add the Material block inside it instead of creating a second product list. With a built-in Product Repeater, edit its selected product display template, available through Website > Designs > Dynamic Displays.

The same record-access pattern works in other repeaters. The variable name must match the repeater's as setting:

Current record variableExample value expression
$contact[? $contact['contact_membership_number'] ?]
$product[? $product['product_material'] ?]
$event[? $event['event_topic'] ?] for a scalar Topic field; format Check Boxes values as shown below.
$attendee[? $attendee['event_attendee_dietary_requirements'] ?]

Use contact and attendee values only in a context where the viewer is allowed to see those records. A custom template must enforce its own visibility rules. If a query selects only specific columns, include the custom field in that selection.

Show multiple selected product features

For a Check Boxes field, use CustomFields::renderFieldValue() to convert the value to text. It handles arrays and serialized selections. Fetch the field definition once before the repeater, then format each product's value inside it:

<logic:variable as="product_features_field" value="[? \Components\Settings\CustomFields::getByComponentAndField('\Components\Commerce\Products', 'product_features') ?]" />
<logic:if test="$product_features_field">
    <data:repeater
        datasource="\Components\Commerce\Products::getAll()"
        datafilter="[? [['product_is_offline', '=', 0]] ?]"
        datalimit="6"
        as="product">
        <article class="product-summary">
            <h3>[? $product['product_title'] ?]</h3>
            <logic:variable as="product_features_text" value="[? \Components\Settings\CustomFields::renderFieldValue($product_features_field, $product['product_features'], 0) ?]" />
            <logic:if test="trim($product_features_text) !== ''">
                <p>Features: [? $product_features_text ?]</p>
            </logic:if>
        </article>
    </data:repeater>
</logic:if>

For example, two selected options appear as Features: Machine washable, Lightweight. An empty selection produces no Features line. The 0 argument disables the formatter's default 20-character limit.

Here $product_features_field is the shared definition, while $product['product_features'] is the current product's value. A repeater over CustomFields::getAll() would iterate field definitions instead of products. Use the owning component as the datasource when you want one item per product, contact or event.

For further details, see the data:repeater reference, CustomFields API and Query Array tutorial.

Advanced: customise field displays

Use the field's Display Code when you want a reusable presentation wherever Oncord invokes that field's display template. For example, you can hide an empty selection, show a Yes / No label or turn a saved web address into a link.

Display Code changes the output of a saved value. It does not change the input form or the stored data, and it is not automatically applied to direct output such as [? $product['product_material'] ?]. You can configure it for a built-in field type without changing the type to Custom UI.

Configure Display Code

  1. Open Settings > Custom Fields in your website's dashboard (/admin/settings/customfields/).
  2. Open the field and confirm its Component, Field Name and Field Type.
  3. Open the Advanced tab and enter your template in Display Code.
  4. Save the field, then view an existing record where the field is displayed.

Copy the existing Display Code before replacing it. Clearing custom Display Code returns the field to its built-in behaviour; some types supply a default display template.

Display Code uses Oncord template markup: HTML, server-side control tags such as <logic:if>, and Scr expressions such as [? $fieldvalue ?]. Do not paste <?php ... ?> or <?= ... ?> blocks into this editor.

Hide an empty event Topic field

Suppose event_topic is a Check Boxes field on \Components\Marketing\Events, labelled Topic. You want selected topics to appear on the event's RSVP page, while an empty selection produces no label or value.

Paste this into the field's Advanced > Display Code:

<logic:variable as="cf_display_text" value="[? \Components\Settings\CustomFields::renderFieldValue($customfield, $fieldvalue, 0) ?]" />
<logic:if test="trim($cf_display_text) !== ''">
    <span class="custom-field">
        <strong>[? $customfield['field_label'] ?]:</strong>
        [? $cf_display_text ?]
    </span>
</logic:if>

renderFieldValue() converts the stored value to display text. It handles both arrays and serialized checkbox values, so you do not need to call unserialize() or guess whether $fieldvalue is an array. Its third argument is the maximum output length: 0 disables truncation; the default is 20 characters.

The condition checks the formatted text before rendering the entire label-and-value block. [? $cf_display_text ?] HTML-encodes the result. The label comes from the field definition, so renaming Topic also updates this display.

Saved valueDisplay result
Null, empty string, empty array, or serialized empty array (a:0:{})No label or value
RunningTopic: Running
Running and Nutrition selectedTopic: Running, Nutrition
The text value 0Topic: 0

Why a raw truthiness check can fail: <logic:if test="$fieldvalue"> sees a:0:{} as a non-empty string, even though it represents no selections. Conversely, a truthiness check hides the meaningful value 0. Formatting first and comparing against an empty string avoids both problems.

Scope of this fix: Display Code controls its own output. A surrounding page template may still add a wrapper or line break. The standard event RSVP template adds a line break outside Display Code and checks the raw value before invoking it. If you need to remove all empty spacing, or display zero values suppressed by that outer check, adjust the surrounding page template too. Changing Display Code alone cannot override an outer condition.

Template syntax and variables

The standard custom-field display consumers provide these variables:

VariableContains
$fieldvalueThe current record's value. Its shape depends on the type and caller; it is not guaranteed to be text or an array.
$customfieldThe field definition, including field_id, field_label, field_ui_type and field_display.

Do not assume variables such as $fieldlabel, $fieldid or $event exist in every display location. Use $customfield['field_label'] for the label. When you invoke a display from your own page, supply the two variables explicitly.

  • test="$fieldvalue !== null" takes a bare Scr expression. Do not wrap a test expression in [? ?].
  • value="[? ... ?]" evaluates the value assigned by logic:variable. A bare method name in this string attribute would be literal text.
  • [? expression ?] HTML-encodes text output. [?? expression ??] emits raw HTML and is reserved for trusted, safely rendered markup.
  • Scr uses + for string concatenation and . for object access. These expressions are not arbitrary PHP blocks.
  • Include the label in your display if you want one. Consumers that invoke Display Code commonly skip their normal label output.

Use double quotes around HTML attributes containing expressions. HTML encoding alone does not validate a link's URL scheme.

More display examples

Plain text or a single-choice value

For a known scalar text, Combo Box or Radio Buttons value, this displays zero while omitting null and empty strings. Use the formatter-based example above for Check Boxes or values whose representation varies.

<logic:if test="$fieldvalue !== null && $fieldvalue !== ''">
    <span class="custom-field">
        <strong>[? $customfield['field_label'] ?]:</strong>
        [? $fieldvalue ?]
    </span>
</logic:if>

Yes / No, including No

An unchecked Yes / No field is a meaningful answer. Keep its label visible when the stored value is zero:

<logic:if test="$fieldvalue !== null && $fieldvalue !== ''">
    <span class="custom-field">
        <strong>[? $customfield['field_label'] ?]:</strong>
        [? $fieldvalue ? 'Yes' : 'No' ?]
    </span>
</logic:if>

A link stored in a text field

For a text field holding a complete web address, this renders a Mentoring Schedule link only for values beginning with https:// or http://. It deliberately excludes relative links and other URL schemes. Configure and validate the field for the URLs your application accepts; this prefix check is not complete URL validation.

<logic:variable as="cf_link_url" value="[? trim((string)$fieldvalue) ?]" />
<logic:if test="strpos($cf_link_url, 'https://') === 0 || strpos($cf_link_url, 'http://') === 0">
    <a class="custom-field-link" href="[? $cf_link_url ?]">Mentoring Schedule</a>
</logic:if>

Style .custom-field-link in the website design if you want a button appearance. Styling classes depend on the design, and website styles may not be present in admin lists or emails.

Formatted Text

Formatted Text and Formatted Text (Extended) values contain HTML. Use renderFieldValueHTML() to preserve allowed formatting while sanitising the value:

<logic:variable as="cf_display_html" value="[? \Components\Settings\CustomFields::renderFieldValueHTML($customfield, $fieldvalue) ?]" />
<logic:if test="trim($cf_display_html) !== ''">
    <div class="custom-field-content">
        <strong>[? $customfield['field_label'] ?]:</strong>
        [?? $cf_display_html ??]
    </div>
</logic:if>

The helper returns HTML suitable for raw output. For other field types it returns encoded text. The condition above skips an empty sanitised result; markup such as an empty paragraph may still be non-empty HTML.

Never pass a stored field value to logic:parse. It parses template code and can execute expressions found in the value. Parse only trusted templates, such as an administrator-authored field_display. Do not output a user-entered value directly with [?? $fieldvalue ??].

Invoke Display Code from a custom template

Use getByComponentAndField() to retrieve the definition. This example belongs inside an event template or repeater where $event already contains the current event record. It does not fetch an event or make an arbitrary page an RSVP page.

<!-- Place inside a template where $event is the current event record. -->
<logic:variable as="customfield" value="[? \Components\Settings\CustomFields::getByComponentAndField('\Components\Marketing\Events', 'event_topic') ?]" />
<logic:if test="$customfield">
    <logic:variable as="fieldvalue" value="[? $event[$customfield['field_id']] ?]" />
    <logic:if test="$customfield['field_display']">
        <logic:parse text="[? $customfield['field_display'] ?]" />
    </logic:if>
    <logic:else>
        [? \Components\Settings\CustomFields::renderFieldValue($customfield, $fieldvalue, 0) ?]
    </logic:else>
</logic:if>

The lookup uses the component class and field name, not the numeric definition ID. The parser receives the trusted field_display template; the stored value is supplied separately as $fieldvalue. If no Display Code is available, this example outputs the formatted value without a label.

Check record access and field visibility before rendering. Keep display templates small and avoid retrieving extra records once per field in a large repeater. A display may be reused in admin lists, profiles and public pages, so avoid duplicate HTML IDs and assumptions about the surrounding layout.

Test and troubleshoot

Save the template and test the real display location with an empty record, one selection, multiple selections, zero where meaningful, a long value, and text containing &, quotes and angle brackets. Also check another location that uses the same field. The editor accepting code does not prove the output is correct.

SymptomCheck
PHP code appears or does not runUse Oncord tags and Scr expressions in Display Code. PHP source belongs in the appropriate PHP editor or component.
A label remains with no selected topicsFormat the value first, then put both label and value inside the condition. A serialized empty array is still a non-empty string.
Array or serialized text appearsDo not echo the raw multi-value field or assume is_array() covers every caller. Use renderFieldValue().
Long values are shortenedPass 0 as the third argument to renderFieldValue().
Zero or No disappearsUse an explicit null/empty-string comparison and inspect conditions in the surrounding page.
Blank spacing remainsInspect wrappers and line breaks outside the display snippet.
The display has no effectConfirm the field was saved, the component and field name match, and the page actually invokes field_display. Direct value output bypasses it.
Changing Display Code does not change an inputUpdate the Field Form, field type or surrounding form configuration for input behaviour.
Formatted text shows HTML tagsUse renderFieldValueHTML() and raw output of that helper's result. Keep plain text encoded.

Related documentation