Custom fields

Extend field definitions and write your own field types.

On this page

Extending a field

A field definition, or a view's entry for a field, can extends another field. It keeps that field's type, label and options and replaces whichever keys it sets; a nested fields map is replaced whole, not merged:

// site/views/post/_index.json
{
    "fields": {
        "summary": { "extends": "description", "label": "Summary" }
    }
}
// site/fields/password/_index.json
{
    "extends": "@phlat/password",
    "options": { "required": true }
}

A name that resolves to nothing, a definition with no type, a type that doesn't exist and a field that extends itself are all errors when the definition is read.

Names from addons

A field or type from an addon carries its owner in the name, the way a view does: @theme/theme is the theme addon's field. A field type is a PHP class in phlat/fieldtypes/{type}/, an addon's fieldtypes/{type}/ or site/fieldtypes/{type}/.

Custom field types

Create site/fieldtypes/{type}/{type}.php, extending the primitive base that matches the JSON shape the type stores: FieldTypeText, FieldTypeNumber, FieldTypeList or FieldTypeObject. Your fields name it bare: "type": "rating".

<?php

namespace Phlat;

class FieldTypeRating extends FieldTypeNumber
{
    public function decode(FieldValue $field): mixed
    {
        // clamp stored value to 0-5
        return max(0, min(5, (int) $field->value));
    }

    public function stars(FieldValue $field): string
    {
        $rating = (int) $this->decode($field);
        return str_repeat('★', $rating) . str_repeat('☆', 5 - $rating);
    }
}
site/fieldtypes/
  rating/
    rating.php   ← class FieldTypeRating extends FieldTypeNumber
    _index.json   ← optional, default search behaviour
    edit.latte   ← optional, admin field-editing UI
    render.latte ← optional, |render output

To control |render output, add render.latte beside the type (site/fieldtypes/{type}/render.latte, or site/fieldtypes/@phlat/{type}/render.latte to override a core type). It receives $field and $value (the decoded value). A render.latte beside a field's own definition, site/fields/{name}/render.latte, takes precedence, and site/views/fields/{view}/{name}.latte wins over both. edit.latte is a separate template for the admin editor.

A type in site/fieldtypes/ is your own and can't share a core type's name. To change a core type's editor templates or defaults, see Overrides; different behaviour is a type of your own.