Skip to the content
Documentation
Help Start
Reading in:
  • German: 9,059 of 9,059 pages
About the help in other languages
All help pages

A custom datatype

Write the datatype expcolor, a color stored as

Download as PDF

Address: https://doc.exponential.earth/exphelp/viewer/examples/cookbook/custom-datatype.md

On this page

A custom datatype

A datatype is the kind of a class attribute: what an editor types, how it is checked, where it is stored and how it is shown. You write expcolor: a color as #rrggbb, chosen with the browser's color field, stored in the object attribute's data_text column, with a default color per class attribute in data_text1.

Prerequisites: the extension of An extension skeleton, switched on; a class to add the attribute to (Create a content class).

1. Where the kernel looks

eZDataType::loadAndRegisterType() looks for <directory>/<type>/<type>type.php in content.ini [DataTypeSettings] RepositoryDirectories[] and in datatypes/ of every extension in ExtensionDirectories[]. A type is offered only when it is in AvailableDataTypes[]. extension/expexample/settings/content.ini.append.php:

<?php /* #?ini charset="utf-8"?

[DataTypeSettings]
# Look for datatypes/<type>/<type>type.php in this extension
ExtensionDirectories[]=expexample
AvailableDataTypes[]=expcolor

*/ ?>

2. The datatype class

extension/expexample/datatypes/expcolor/expcolortype.php. The file registers the class with eZDataType::register() at its end, as every kernel datatype does:

<?php
/**
 * The datatype expcolor: a color as #rrggbb, stored in data_text of the object attribute.
 * The class attribute keeps its default color in data_text1.
 *
 * @copyright Copyright (C) 2026 Example Company
 * @license GNU General Public License v2.0 (or any later version)
 * @package expexample
 */

class expColorType extends eZDataType
{
    const DATA_TYPE_STRING = 'expcolor';
    const DEFAULT_FIELD = 'data_text1';

    public function __construct()
    {
        parent::__construct( self::DATA_TYPE_STRING, ezpI18n::tr( 'extension/expexample', 'Color', 'Datatype name' ),
                             array( 'serialize_supported' => true,
                                    'object_serialize_map' => array( 'data_text' => 'color' ) ) );
    }

    /** #rrggbb in lower case, or '' when $value is not a color */
    public static function normalize( $value )
    {
        $value = strtolower( trim( (string)$value ) );
        return preg_match( '/^#[0-9a-f]{6}$/', $value ) ? $value : '';
    }

    /** A new attribute gets the class default; a new version copies the previous one */
    public function initializeObjectAttribute( $contentObjectAttribute, $currentVersion, $originalContentObjectAttribute )
    {
        if ( $currentVersion != false )
        {
            $contentObjectAttribute->setAttribute( 'data_text', $originalContentObjectAttribute->attribute( 'data_text' ) );
            return;
        }
        $default = $contentObjectAttribute->contentClassAttribute()->attribute( self::DEFAULT_FIELD );
        $contentObjectAttribute->setAttribute( 'data_text', self::normalize( $default ) );
    }

    public function validateObjectAttributeHTTPInput( $http, $base, $contentObjectAttribute )
    {
        $name = $base . '_expcolor_' . $contentObjectAttribute->attribute( 'id' );
        $value = $http->hasPostVariable( $name ) ? trim( $http->postVariable( $name ) ) : '';
        if ( $value === '' )
        {
            if ( $contentObjectAttribute->validateIsRequired() )
            {
                $contentObjectAttribute->setValidationError( ezpI18n::tr( 'extension/expexample', 'Input required.' ) );
                return eZInputValidator::STATE_INVALID;
            }
            return eZInputValidator::STATE_ACCEPTED;
        }
        if ( self::normalize( $value ) === '' )
        {
            $contentObjectAttribute->setValidationError( ezpI18n::tr( 'extension/expexample', 'Write the color as #rrggbb.' ) );
            return eZInputValidator::STATE_INVALID;
        }
        return eZInputValidator::STATE_ACCEPTED;
    }

    public function fetchObjectAttributeHTTPInput( $http, $base, $contentObjectAttribute )
    {
        $name = $base . '_expcolor_' . $contentObjectAttribute->attribute( 'id' );
        if ( !$http->hasPostVariable( $name ) )
            return false;
        $contentObjectAttribute->setAttribute( 'data_text', self::normalize( $http->postVariable( $name ) ) );
        return true;
    }

    public function fetchClassAttributeHTTPInput( $http, $base, $classAttribute )
    {
        $name = $base . '_expcolor_default_' . $classAttribute->attribute( 'id' );
        if ( $http->hasPostVariable( $name ) )
            $classAttribute->setAttribute( self::DEFAULT_FIELD, self::normalize( $http->postVariable( $name ) ) );
        return true;
    }

    /** {$attribute.content} in templates */
    public function objectAttributeContent( $contentObjectAttribute )
    {
        return $contentObjectAttribute->attribute( 'data_text' );
    }

    public function hasObjectAttributeContent( $contentObjectAttribute )
    {
        return $contentObjectAttribute->attribute( 'data_text' ) !== '';
    }

    public function metaData( $contentObjectAttribute )
    {
        return $contentObjectAttribute->attribute( 'data_text' );
    }

    public function title( $contentObjectAttribute, $name = null )
    {
        return $contentObjectAttribute->attribute( 'data_text' );
    }

    public function isIndexable()
    {
        return true;
    }

    public function sortKey( $contentObjectAttribute )
    {
        return $contentObjectAttribute->attribute( 'data_text' );
    }

    public function sortKeyType()
    {
        return 'string';
    }

    /** CSV import and export (ezcsvexport.php, ezcsvimport.php) */
    public function toString( $contentObjectAttribute )
    {
        return $contentObjectAttribute->attribute( 'data_text' );
    }

    public function fromString( $contentObjectAttribute, $string )
    {
        $contentObjectAttribute->setAttribute( 'data_text', self::normalize( $string ) );
        return true;
    }
}

eZDataType::register( expColorType::DATA_TYPE_STRING, 'expColorType' );

What the kernel calls, and when:

MethodCalled
initializeObjectAttribute()a new object, or a new version ($currentVersion set)
validateObjectAttributeHTTPInput()on Send for publishing and Store draft; an eZInputValidator::STATE_*
fetchObjectAttributeHTTPInput()after validation, to store the posted value
fetchClassAttributeHTTPInput()when the class edit form is stored
objectAttributeContent()$attribute.content in templates
hasObjectAttributeContent()$attribute.has_content
metaData(), isIndexable()the text the search engine indexes
title()the attribute in the object name pattern (<color>)
sortKey(), sortKeyType()sorting children by this attribute
toString(), fromString()CSV import and export, and other scripts that copy values

The base class implements the rest (storing, deleting, versioning of the plain columns). A datatype with a table of its own also implements storeObjectAttribute(), deleteStoredObjectAttribute() and the serialize methods for packages.

3. The templates

The kernel finds a datatype's templates by its identifier. All four go into the extension's design (extension/expexample/design/standard/templates/).

content/datatype/edit/expcolor.tpl, the field in the object edit form; the input name must be the one the class reads (<base>_expcolor_<attribute id>):

{* The edit field of an expcolor attribute *}
{default attribute_base='ContentObjectAttribute'}
<input id="ezcoa-{$attribute.contentclassattribute_id}_{$attribute.contentclass_attribute_identifier}"
       class="box ezcc-{$attribute.object.content_class.identifier} ezcca-{$attribute.object.content_class.identifier}_{$attribute.contentclass_attribute_identifier}"
       type="color"
       name="{$attribute_base}_expcolor_{$attribute.id}"
       value="{first_set( $attribute.content, '#000000' )|wash}" />
{/default}

content/datatype/view/expcolor.tpl, what {attribute_view_gui attribute=...} shows:

{* The view of an expcolor attribute: the color as text; a design gives it a swatch with a class *}
{if $attribute.has_content}
<span class="expexample-color" data-color="{$attribute.content|wash}">{$attribute.content|wash}</span>
{/if}

The color is handed to the design as a data- attribute, not as style="": a style sheet or a script of the design draws the swatch, which keeps the page within a Content Security Policy.

class/datatype/edit/expcolor.tpl, the default on the class edit page (the kernel posts it with the base ContentClass):

{* The class edit form of an expcolor attribute: its default color *}
<div class="block">
    <label for="ContentClass_expcolor_default_{$class_attribute.id}">{'Default color'|i18n( 'extension/expexample' )}:</label>
    <input id="ContentClass_expcolor_default_{$class_attribute.id}" type="color"
           name="ContentClass_expcolor_default_{$class_attribute.id}"
           value="{first_set( $class_attribute.data_text1, '#000000' )|wash}" />
</div>

class/datatype/view/expcolor.tpl, the same on the class view page:

{* The class view of an expcolor attribute *}
<div class="block">
    <label>{'Default color'|i18n( 'extension/expexample' )}:</label>
    <p>{$class_attribute.data_text1|wash}</p>
</div>

4. Switch it on and use it

php -l extension/expexample/datatypes/expcolor/expcolortype.php
php bin/php/ezcache.php --clear-all --allow-root-user

The datatype file is included by the loader, not by the autoload array; restart Velocity (or deploy) so that its workers load the new settings.

  1. Setup > Classes, edit Note, Add attribute: Color is in the list. Name it Color, identifier color, choose a default, OK.
  2. Edit a Note: the color field is there; Send for publishing.
  3. In a template: {$node.data_map.color.content} is #3366cc; {attribute_view_gui attribute=$node.data_map.color} renders the view template.

Checked for this page (2026-10-18): php -l on the class; bin/php/eztemplatecheck.php reports the four templates valid (syntax); every overridden method has the signature of kernel/classes/ezdatatype.php. The datatype was not run against a database for this page.

Common mistakes

SymptomCause
The type is not offered on the class edit pagenot in AvailableDataTypes[], or ExtensionDirectories[] missing; the debug output names the directories searched
The value is never storedthe input name in the edit template differs from the one the class reads
A required attribute accepts an empty valuevalidateIsRequired() is not asked
Changing DATA_TYPE_STRING laterevery existing attribute keeps the old string in ezcontentclass_attribute.data_type_string; choose it once

Derived from

  • kernel/classes/ezdatatype.php (loadAndRegisterType(), register(), the method list)
  • kernel/classes/datatypes/ezboolean/ezbooleantype.php, kernel/classes/datatypes/ezstring/ezstringtype.php
  • design/standard/templates/content/datatype/edit/ezboolean.tpl, design/standard/templates/class/datatype/edit/ezstring.tpl
  • settings/content.ini [DataTypeSettings]

Something wrong on this page? Report an error

Documentation license

This documentation is free documentation, licensed under the GNU Free Documentation License 1.3 or later.

Exponential Copyright © 1998-2026 Exponential Foundation and 7x