Field Types

Plugins can provide custom field types by creating a class that implements craft\base\FieldInterface (opens new window) and craft\base\FieldTrait (opens new window). The class will serve both as a way to communicate various things about your field type (with static methods), and as a model that fields of its type will be instantiated with.

# Field Class

Scaffold a field type with the generator:

php craft make field-type --plugin=my-plugin

If you would prefer to write a field class from scratch, it should extend craft\base\Field (opens new window). The base implementation provides some sane defaults, but lacks many specifics that differentiate your field.

Refer to Craft’s own field classes (located in vendor/craftcms/cms/src/fields/, or the craft\fields namespace) for examples. You may be able to start with a more specific base field type, if your field’s anticipated value type(s) are compatible.

# Registering Custom Field Types

Once you have created your field class, you must register it with the Fields service so Craft will know about it when populating the list of available field types:

<?php
namespace mynamespace;

use craft\events\RegisterComponentTypesEvent;
use craft\services\Fields;
use yii\base\Event;

class Plugin extends \craft\base\Plugin
{
    public function init()
    {
        Event::on(
            Fields::class,
            Fields::EVENT_REGISTER_FIELD_TYPES,
            function(RegisterComponentTypesEvent $event) {
                $event->types[] = MyField::class;
            }
        );

        // ...
    }

    // ...
}

The generator takes care of this for you!

# Supporting Delta Saves

If your field type does any processing from afterElementSave() (opens new window) or afterElementPropagate() (opens new window), you can improve performance by skipping processing when the field’s value is unchanged.

You can tell whether field content has changed by calling isFieldDirty() (opens new window) on the element.

public function afterElementSave(ElementInterface $element, bool $isNew): void
{
    if ($element->isFieldDirty()) {
        // logic for handling saved element
    }

    parent::afterElementSave($element, $isNew);
}

# Storing Content

By default, a field type’s hasContentColumn() (opens new window) method returns true, meaning Craft will create one or more fields for it in the database’s content table.

The field type’s getContentColumnType() (opens new window) method can return either a single column type, or a key-value array of multiple handles and types (opens new window).

public function getContentColumnType(): array|string
{
    return \yii\db\Schema::TYPE_STRING;
}

See Craft’s Date (opens new window) field for an example that uses two columns when it’s configured to store a timezone.

Any column Craft creates in the content table will automatically get a random 9-character suffix like _ycpsotpa. This ensures column names are unique even in the rare case that identical handles are used. You can get this suffix from any field instance from its columnSuffix property.

If you’d rather have your custom field type manage its own content in the database, you can return false for hasContentColumn()—just know that you’ll have to manually account for several things you otherwise get for free when Craft manages its content fields in the database:

  • Automatic creation, duplication, retrieval, and deletion of field content in the database.
  • Any core migrations or improvements that impact the content table.
  • Automatic support for sites and localization.
  • Automatic inclusion of content fields in element queries and GraphQL.