Berdir gave the correct answer, that a constraint is the correct way to go about adding validation to a field in Drupal 8. Here is an example.
In the example below, I will be working with a node of type
podcast, that has the single value field
field_podcast_duration. The value for this field needs to be formatted as HH:MM:SS (hours, minutes and seconds).
To create a constraint, two classes need to be added. The first is the constraint definition, and the second is the constraint validator. Both of these are plugins, in the namespace of
First, the constraint definition. Note that the plugin ID is given as 'PodcastDuration', in the annotation (comment) of the class. This will be used further down.
* Checks that the submitted duration is of the format HH:MM:SS
* id = "PodcastDuration",
* label = @Translation("Podcast Duration", context = "Validation"),
class PodcastDurationConstraint extends Constraint
// The message that will be shown if the format is incorrect.
public $incorrectDurationFormat = 'The duration must be in the format HH:MM:SS or HHH:MM:SS. You provided %duration';
Next, we need to provide the constraint validator. This name of this class will be the class name from above, with
Validator appended to it:
* Validates the PodcastDuration constraint.
class PodcastDurationConstraintValidator extends ConstraintValidator
public function validate($items, Constraint $constraint)
// This is a single-item field so we only need to
// validate the first item
$item = $items->first();
// If there is no value we don't need to validate anything
// Check that the value is in the format HH:MM:SS
// The value is an incorrect format, so we set a 'violation'
// aka error. The key we use for the constraint is the key
// we set in the constraint, in this case $incorrectDurationFormat.
$this->context->addViolation($constraint->incorrectDurationFormat, ['%duration' => $item->value]);
Finally, we need to tell Drupal to use our constraint on
field_podcast_duration on the
podcast node type. We do this in
function HOOK_entity_bundle_field_info_alter(&$fields, EntityTypeInterface $entity_type, $bundle)