Drupal 8: creating a custom Field Type
If you need to add complex fields to a Content Type, for example a group of fields composed by one text, one number and one image, maybe with the possibility to repeat it multiple times inside a content, the simplest and fastest way to do it is using a combination of these great Drupal modules (or maybe all together also): Paragraphs, Field collection, Field group.
If you instead are searching for some special field type, you should take a look to one of these wonderful Field Types modules if you find what you need: Double field, Color field, Address, Taxonomy container, Advanced text formatter.
But if none of the modules above fits your needs, maybe because you want to create some really particular field type, or you want to have more control as possible on the field type, about it is validated, stored, edited and displayed, or you need to create only a small thing and you don’t want to install a generic module to do it, or for some other reason, then you may want to create your own custom Field Type.
Define a custom Field Type
Create a new custom module
A Field Type in Drupal is defined as a module, then we need to create a new custom module.
Create the modules/custom
directory if it doesn’t exists.
Inside the custom
directory create the following folder structure that will contains the Field Type, in this case named address
:
address/ + src/ | + Plugin/ | | + Field/ | | | + FieldFormatter/ | | | + FieldType/ | | | + FieldWidget/ + address.info.yml
IMPORTANT: the module name must be lower case otherwise Drupal will ignore it.
File .info.yml
Inside the file with extension .info.yml
we set some informations about the module, as the module name, the description, the package that must be Field types
and the list of dependencies that must contains the value field
:
name: Address
description: Defines a simple address field type.
# The type must be 'module' and the package must be 'Field types'
type: module
package: Field types
core: 8.x
# The list of dependencies must contains the 'field' value
dependencies:
- field
Field type
Inside the folder src/Plugin/Field/FieldType
create a new php file that will contains the Field Type definition:
src/Plugin/Field/FieldType/Address.php
<?php
namespace Drupal\address\Plugin\Field\FieldType;
use Drupal\Core\Field\FieldItemBase;
use Drupal\Core\TypedData\DataDefinition;
use Drupal\Core\Field\FieldStorageDefinitionInterface as StorageDefinition;
/**
* Plugin implementation of the 'address' field type.
*
* @FieldType(
* id = "Address",
* label = @Translation("Address"),
* description = @Translation("Stores an address."),
* category = @Translation("Custom"),
* default_widget = "AddressDefaultWidget",
* default_formatter = "AddressDefaultFormatter"
* )
*/
class Address extends FieldItemBase {
/**
* Field type properties definition.
*
* Inside this method we defines all the fields (properties) that our
* custom field type will have.
*
* Here there is a list of allowed property types: https://goo.gl/sIBBgO
*/
public static function propertyDefinitions(StorageDefinition $storage) {
$properties = [];
$properties['street'] = DataDefinition::create('string')
->setLabel(t('Street'));
$properties['city'] = DataDefinition::create('string')
->setLabel(t('City'));
return $properties;
}
/**
* Field type schema definition.
*
* Inside this method we defines the database schema used to store data for
* our field type.
*
* Here there is a list of allowed column types: https://goo.gl/YY3G7s
*/
public static function schema(StorageDefinition $storage) {
$columns = [];
$columns['street'] = [
'type' => 'char',
'length' => 255,
];
$columns['city'] = [
'type' => 'char',
'length' => 255,
];
return [
'columns' => $columns,
'indexes' => [],
];
}
/**
* Define when the field type is empty.
*
* This method is important and used internally by Drupal. Take a moment
* to define when the field fype must be considered empty.
*/
public function isEmpty() {
$isEmpty =
empty($this->get('street')->getValue()) &&
empty($this->get('city')->getValue());
return $isEmpty;
}
} // class
In the comment above the class there is the @FieldType annotation. This annotation defines the new Field Type and you must set some properties here, as a field type id
, a label
, etc, and two properties, default_widget
and default_formatter
, that are respectively a widget for editing the field type and a formatter for displaying the field type. We define both the widget and the formatter in a moment.
The class Address
defines the custom Field Type: its properties (fields) with the method propertyDefinitions
, how the properties should be stored in the database in the method schema
and the definition of empty field with the method isEmpty
.
Inside this class you can define also some other logic for the Field Type, as the validation logic with the getConstraints
method. Here there is an example.
Field widget
Inside the folder src/Plugin/Field/FieldWidget
create a new php file containing the field widget definition:
src/Plugin/Field/FieldWidget/AddressDefaultWidget.php
<?php
namespace Drupal\address\Plugin\Field\FieldWidget;
use Drupal;
use Drupal\Core\Field\FieldItemListInterface;
use Drupal\Core\Field\WidgetBase;
use Drupal\Core\Form\FormStateInterface;
/**
* Plugin implementation of the 'AddressDefaultWidget' widget.
*
* @FieldWidget(
* id = "AddressDefaultWidget",
* label = @Translation("Address select"),
* field_types = {
* "Address"
* }
* )
*/
class AddressDefaultWidget extends WidgetBase {
/**
* Define the form for the field type.
*
* Inside this method we can define the form used to edit the field type.
*
* Here there is a list of allowed element types: https://goo.gl/XVd4tA
*/
public function formElement(
FieldItemListInterface $items,
$delta,
Array $element,
Array &$form,
FormStateInterface $formState
) {
// Street
$element['street'] = [
'#type' => 'textfield',
'#title' => t('Street'),
// Set here the current value for this field, or a default value (or
// null) if there is no a value
'#default_value' => isset($items[$delta]->street) ?
$items[$delta]->street : null,
'#empty_value' => '',
'#placeholder' => t('Street'),
];
// City
$element['city'] = [
'#type' => 'textfield',
'#title' => t('City'),
'#default_value' => isset($items[$delta]->city) ?
$items[$delta]->city : null,
'#empty_value' => '',
'#placeholder' => t('City'),
];
return $element;
}
} // class
The @FieldWidget annotation defines the field widget. In this annotation you should set the widget id
that is the value used for the default_widget
property in the @FieldType annotation. We used the class name as id
but you can use also some different name. The field_types
property should list all the field types allowed for this widget, and here we can set the field type id
previously defined.
With the formElement
method you can define the form that will be used to edit the Address
field type. As for the field type definition, you can define complex logic here and create special forms for your field type. Some examples are here and here.
Field formatter
Inside the folder src/Plugin/Field/FieldFormatter
create a new php containing the field formatter definition:
src/Plugin/Field/FieldFormatter/AddressDefaultFormatter.php
<?php
namespace Drupal\address\Plugin\Field\FieldFormatter;
use Drupal\Core\Field\FieldItemListInterface;
use Drupal\Core\Field\FormatterBase;
use Drupal;
/**
* Plugin implementation of the 'AddressDefaultFormatter' formatter.
*
* @FieldFormatter(
* id = "AddressDefaultFormatter",
* label = @Translation("Address"),
* field_types = {
* "Address"
* }
* )
*/
class AddressDefaultFormatter extends FormatterBase {
/**
* Define how the field type is showed.
*
* Inside this method we can customize how the field is displayed inside
* pages.
*/
public function viewElements(FieldItemListInterface $items, $langcode) {
$elements = [];
foreach ($items as $delta => $item) {
$elements[$delta] = [
'#type' => 'markup',
'#markup' => $item->street . ', ' . $item->city
];
}
return $elements;
}
} // class
The @FieldFormatter annotation defines the field formatter and it is very similar to the @FieldWidget annotation.
With the viewElements
method you can define how the field type will be displayed inside a page. If you want to use some HTML to display the field type you can also use a custom theme for this, and avoid to put HTML tags inside the PHP code. Here you can find an example using a theme.
Use the Field Type
Once created the Field Type you should install the module in Drupal 8 then you can use your custom Field Type.
Install with the admin panel
Go to Extends, search your module in the list, select it, then click on “Install” at the bottom of the page.
Install with Drupal console
If you installed the Drupal console tool you can use these commands to install and uninstall your Field Type (module):
$ # Install a module
$ drupal module:install [module_name]
$ # Remove a module
$ drupal module:uninstall [module_name]
Get the code from GitHub
You can get the code used in this post from our GitHub repository here: https://github.com/netgloo/drupal-samples/tree/master/address
References
Tutorials
http://www.ixis.co.uk/blog/drupal-8-creating-field-types-multiple-values
https://www.computerminds.co.uk/drupal-code/drupal-8-creating-custom-field-part-1-field-type
http://capgemini.github.io/drupal/writing-custom-fields-in-drupal-8/
https://www.drupal.org/node/2302735
https://www.drupal.org/node/2620964
Properties, columns and form elements types
https://goo.gl/XVd4tA
https://www.drupal.org/node/159605
https://api.drupal.org/api/drupal/elements
Other references
https://api.drupal.org/api/drupal/core!modules!field!field.api.php/group/field_types/8
https://www.drupal.org/node/2116781
http://www.sitepoint.com/creating-custom-field-formatters-drupal-8/
https://api.drupal.org/api/drupal/core!lib!Drupal!Core!Database!database.api.php/group/schemaapi/8
-
Scarwr McScary
-
Andrea
-
-
Nicola
-
Lalit Garg
-
Critter Power