Base fields

Base fields are the fields closest to a given entity type, things like the title, creation/modification date, publication status, and so on. They are defined in the entity type class as BaseFieldDefinition implementations and are installed in the database based on these definitions. Once installed, they are no longer configurable from a storage point of view from the UI (except in some cases, in which certain aspects can be overridden). Additionally, some display and form widget configuration changes can still be made (also depending on whether the individual definitions allow this).

Let's check out the Node entity type's baseFieldDefinitions() method and see an example of a base field definition:

$fields['title'] = BaseFieldDefinition::create('string')
->setLabel(t('Title'))
->setRequired(TRUE)
->setTranslatable(TRUE)
->setRevisionable(TRUE)
->setSetting('max_length', 255)
->setDisplayOptions('view', [
'label' => 'hidden',
'type' => 'string',
'weight' => -5,
])
->setDisplayOptions('form', [
'type' => 'string_textfield',
'weight' => -5,
])
->setDisplayConfigurable('form', TRUE);

This is the definition of the Node title field. We can deduce that is of of the string type due to the argument passed to the create() method of the BaseFieldDefinition class. The latter is a complex data definition class on top of the TypedData API.

Other common types of fields that can be defined are boolean, integer, float, timestamp, datetime, entity_reference, text_long, and many others. You can find out what field types you can use by checking the available FieldType plugins provided by Drupal core and any other modules. These are the same types of fields that can be used by configurable fields in the UI. In a later chapter, we will see how we can write our own custom field type.

The field definition can have a number of options that may also differ depending on the type of field being defined. I will skip the obvious ones here and jump to the setTranslatable() and setRevisionable() methods and ask you to remember when we saw earlier how the Node entity type plugin annotation indicated that Nodes will be translatable and revisionable. This is where the fields themselves are configured to that effect. Without these settings, they'd be left out of the translation capability and revisions.

If you take a look at how the baseFieldDefinitions() method starts, you'll see that it inherits some fields from the parent class as well. This is where common field definitions are inherited, which allow for the entity type to be revisionable and publishable.

The setSetting() method is used to provide various options to the field. In this case, it's used to indicate the maximum length, which is also mirrored in the table column in the database. Then, we have the display options that configure the view formatter and form widget the field should use. They reference plugin IDs of the type FieldFormatter (string) and FieldWidget (string_textfield) plugins, respectively. In a later chapter, we will see how we can define our own field plugins that can be used for both base and configurable fields.

Lastly, we have the setDisplayConfigurable() method which is used to enable/disable configuration changes on the form widget or display through the UI. In this case, only the form widget is exposed to changes.

Not all these options and configurations are always used or mandatory. It depends on what type of field we are defining, how we want the field to be configured, and whether defaults are okay for us. An important option that can be used on all field types is cardinalitywhether the field can have more than one value of the same type. This allows a field to store multiple values that follow the same data definition on that entity field.

If we create our own entity type and want to later add or modify a base field, we can do that in the same place as we originally defined themin the entity class. However, for entities that do not "belong" to us, we need to implement some hooks in order to contribute with our own changes. To provide a new base field definition to an existing entity type, we can implement hook_entity_base_field_info() in our module and return an array of BaseFieldDefinition items just as we saw before in the Node entity type. Alternatively, we can implement hook_entity_base_field_info_alter() and alter existing base field definitions to our liking. Do keep in mind that this latter hook might be changed in the future, although at the time of writing, no great priority has been given to that.