Dynamic Default and Allowed Values for List Fields in Drupal 8

Drupal has an excellent field system with unique field types for storing just about every kind of data. When entering data that requires selecting an option from a predefined set of values with an optional default value the List field is often used.

The List field type provides a UI for entering allowed values and choosing a default value. This is great when the option set is small and the default value is always the same. However, often the editorial workflow calls for options that vary per content type and a default that is not consistent.

Despite the UI giving no indication of this functionality existing, the List field supports the setting of dynamic allowed values and dynamic default values.

static allowed values in UI

For our scenario, let’s suppose that there is an Alignment field (field_alignment), that had “Left” and “Right” as options. However, the Article content type was added, and it needed a “Center” alignment option, while the other content types needed to only have the original options. Additionally, existing content defaults to “Right” alignment, but the Article content type should use “Center” as the default value.

With that in mind, let’s walk through how to do this in Drupal 8 using the allowed_values_function and default_value_callback field configuration values.

Allowed Values

First we will alter our allowed values to add “Center” as an alignment option for Article content.

In the field instance definition configuration file, set the allowed_values_function key. Note that the allowed_values entries can be removed once an allowed_values_function is in place.

field.storage.node.field_alignment.yml

...
type: list_string
settings:
allowed_values: { }
allowed_values_function: 'example_allowed_values_function'
module: options
locked: false
cardinality: 1
...

Now setup the allowed values function and place it anywhere in the global namespace (e.g. a .module file):

use Drupal\Core\Entity\ContentEntityInterface;
use Drupal\field\Entity\FieldStorageConfig;

/**
* Set dynamic allowed values for the alignment field.
*
* @param \Drupal\field\Entity\FieldStorageConfig $definition
* The field definition.
* @param \Drupal\Core\Entity\ContentEntityInterface|null $entity
* The entity being created if applicable.
* @param bool $cacheable
* Boolean indicating if the results are cacheable.
*
* @return array
* An array of possible key and value options.
*
* @see options_allowed_values()
*/

function example_allowed_values_function(FieldStorageConfig $definition, ContentEntityInterface $entity = NULL, $cacheable) {
$options = [
'left' => 'Left',
'right' => 'Right',
];
// Add a custom alignment option for Article nodes.
if ($entity->bundle() == 'article') {
$options['center'] = 'Center';
}

return $options;
}

Once the function is in place, the UI will reflect the change and disable manual editing of the allowed options.

dynamic allowed values in UI

Default Values

Now we will leverage our newly added “Center” alignment option and set it as the default value for this field when authoring Article content.

In the field base definition configuration file, set the default_value_callback.

field.field.node.article.field_alignment.yml

...
translatable: false
default_value: { }
default_value_callback: 'example_default_value_function'
settings: { }
field_type: list_string
...

Next, setup the default value function anywhere in the global namespace as follows:

use Drupal\Core\Entity\ContentEntityInterface;
use Drupal\Core\Field\FieldDefinitionInterface;

/**
* Sets the default value for the alignment field.
*
* @param \Drupal\Core\Entity\ContentEntityInterface $entity
* The entity being created.
* @param \Drupal\Core\Field\FieldDefinitionInterface $definition
* The field definition.
*
* @return array
* An array of default value keys with each entry keyed with the “value” key.
*
* @see \Drupal\Core\Field\FieldConfigBase::getDefaultValue()
*/

function example_default_value_function(ContentEntityInterface $entity, FieldDefinitionInterface $definition) {
$default = 'right';
// Article nodes should default to center alignment.
if ($entity->bundle() == 'article') {
$default = 'center';
}

return [
['value' => $default],
];
}

With all of this in place, re-import the configuration and test it out. The authoring interface should now reflect the alternate allowed values per content type as well as the custom default value. So the next time the List field doesn’t quite meet your needs, give it a second chance, it might just be the right field for the job!