Field types have moved from hook implementations in Drupal 7 to plugins in Drupal 8.
Field types registry
The functions used to access properties about available field types are moved to methods on the FieldTypePluginManager:
| field_info_field_types() | \Drupal::service('plugin.manager.field.field_type')->getDefinitions() |
|---|---|
| field_info_field_types($type) | \Drupal::service('plugin.manager.field.field_type')->getDefinition($type) |
| field_info_field_settings($type) | \Drupal::service('plugin.manager.field.field_type')->getDefaultStorageSettings($type) |
| field_info_instance_settings($type) | \Drupal::service('plugin.manager.field.field_type')->getDefaultFieldSettings($type) |
Field types implementations
The plugin classes implementing field types are directly the "field item" classes used by "field value" objects in Drupal 8's new Entity API: the class providing the 'image' field type is \Drupal\image\Plugin\Field\FieldType\ImageItem, and it is the class of the object holding the field value at $node->field_image[0].
In short: providing a field type is providing a type of "field value", and the code in the various methods is the value object specifying its own behavior.
(in more detail, the "field type" plugins are automatically added as "data type" plugins through a "plugin derivative", and the objects are only ever instantiated as "typed data" objects rather than as "field type plugins").
The biggest conceptual shift from Drupal 7 is that field item structures are no longer dumb arrays with raw data. They are classed objects, and each field type has full control over what happens with its field values. Instead of massaging field item arrays from the outside (e.g. hook_field_load()), they can provide computed field property classes and implement methods on the item and item list classes to act when something happens. Handbook documentation: http://drupal.org/node/2112677
More information about the Entity API in Drupal 8 can be found in the handbook.
To port existing field types, create a class in a file like '{module}/src/Plugin/Field/FieldType/{FieldType}.php. The class must implement Drupal\Core\Field\FieldItemInterface (in most cases, you will want to extend the Drupal\Core\Field\ConfigFieldItemBase base class).
The following hooks are removed and are now methods in Drupal\Core\Field\ConfigFieldItemInterface:
| hook_field_info() | Replaced by annotation-based plugin discovery, using the \Drupal\Core\Field\Annotation\FieldType annotation class + FieldItemInterface::defaultStorageSettings() / FieldItemInterface::defaultFieldSettings() (static methods) |
|---|---|
| hook_field_schema() | FieldItemInterface::schema() (static method) |
| hook_field_settings_form() | FieldItemInterface::storageSettingsForm() |
| hook_field_instance_settings_form() | FieldItemInterface::fieldSettingsForm() |
| hook_field_is_empty() | ComplexDataInterface::isEmpty() |
| hook_field_presave() | FieldItemInterface::preSave() |
| hook_field_insert() | FieldItemInterface::insert() |
| hook_field_update() | FieldItemInterface::update() |
| hook_field_delete() | FieldItemInterface::delete() |
| hook_field_delete_revision() | FieldItemInterface::deleteRevision() |
| hook_field_validate() | TypedDataInterface::getConstraints() (field validation moves on top of the existing Entity validation with Symfony constraints) |
| hook_field_load() | Removed. Field types can add computed properties or methods to their field item classes instead. See https://drupal.org/node/2112677. |
| hook_field_prepare_view() | Removed, in favour of prepareView() on Formatters. Field types that implemented specific prepare_view steps with this hook should consider providing a base prepareView() implementation in an abstract base formatter class instead. |
| hook_field_prepare_translation() | Dropped - was only related to the old (node-only) translation model. |
| hook_field_prepare_translation_alter() | Dropped - was only related to the old (node-only) translation model. |
Field types can also specifiy a custom class to use for the field item list by specifying the list_class property in the @FieldType annotation. By default, \Drupal\Core\Field\FieldItemList is used for fields.
Contrary to the old D7 hooks above, the methods do not receive the parent entity or the langcode of the field values as parameters. If needed, those can be accessed by the getEntity() and getLangcode() methods on the FieldItemList and FieldItem classes.
If a field type doesn't want to be available in the "Add new field" UI, it can specify 'no_ui' = TRUE in its definition (typically in the @FieldType annotation). Such field types can only be used for
>base fields, or added programmatically.
Code example:
Example for the email field.
Drupal 7
/**
* Implements hook_field_info().
*/
function email_field_info() {
return array(
'email' => array(
'label' => 'Email',
'description' => t('This field stores and renders email addresses.'),
'default_widget' => 'email_textfield',
'default_formatter' => 'email_default',
'property_type' => 'text',
),
);
}
/**
* Implements hook_field_schema().
*/
function email_field_schema($field) {
return array(
'columns' => array(
'email' => array(
'type' => 'varchar',
'length' => 255,
'not null' => FALSE,
),
),
);
}
// ... other field API hooks ...
Drupal 8
namespace Drupal\telephone\Plugin\Field\FieldType;
use Drupal\Core\Field\FieldItemBase;
use Drupal\Core\Field\FieldStorageDefinitionInterface;
use Drupal\Core\TypedData\DataDefinition;
/**
* Plugin implementation of the 'telephone' field type.
*
* @FieldType(
* id = "telephone",
* label = @Translation("Telephone number"),
* description = @Translation("This field stores a telephone number in the database."),
* default_widget = "telephone_default",
* default_formatter = "string"
* )
*/
class TelephoneItem extends FieldItemBase {
/**
* Definitions of the contained properties.
*
* @var array
*/
static $propertyDefinitions;
/**
* {@inheritdoc}
*/
public static function schema(FieldStorageDefinitionInterface $field_definition) {
return array(
'columns' => array(
'value' => array(
'type' => 'varchar',
'length' => 256,
'not null' => FALSE,
),
),
);
}
/**
* {@inheritdoc}
*/
public static function propertyDefinitions(FieldStorageDefinitionInterface $field_definition) {
$property_definitions['value'] = DataDefinition::create('string')
->setLabel(t('Telephone number'));
return $property_definitions;
}
}
Comments
hook_field_load() ->
Uh... what now? How does that explain how to convert
hook_field_load()to D8? From D7:https://api.drupal.org/api/drupal/modules%21field%21field.api.php/functi...
Some explanation courtesy of
Some explanation courtesy of berdir in #drupal-contribute regarding hook_field_load() in Drupal 7, and how to achieve the same thing(s) now in Drupal 8:
Also worth noting: in order to implement getCacheData() within a field item class (a child class of FieldItemBase), that class also needs to implement PrepareCacheInterface.
I fleshed out the handbook
I fleshed out the handbook page for computed properties: http://drupal.org/node/2112677
Hopefully it's all correct. Feel free to update if there are any mistakes. I set the status of it to "Needs technical review".
getPropertyDefinitions()
getPropertyDefinitions() needs to return an array of DataDefinition objects.
#2112239: Convert base field and property definitions