The Entity API allows the definition of entity level constraints, which can validate across multiple field values (details to be figured out in #2105797: Add CompositeConstraintBase so that constraints involving multiple fields, such as CommentNameConstraint, can be discovered) or general validation logic that needs to run independent of the fields being edited. In Drupal 8 an example for this would be validating the 'changed' field to prevent concurrent editing of entities like nodes, for which core provides the EntityChanged constraint.
Entity level constraints can be defined via the entity annotation or added to an existing entity type via the hook_entity_type_build() (for adding) and hook_entity_type_alter()(for changing) hooks.
This is how the annotation looks like:
/**
* Defines a test class for testing the definition of entity level constraints.
*
* @ContentEntityType(
* id = "entity_example",
* label = @Translation("Example"),
* entity_keys = {
* "id" = "id",
* "uuid" = "uuid",
* "bundle" = "type",
* "label" = "name"
* },
* constraints = {
* "ExampleConstraint" = {}
* }
* )
*/
class EntityExample extends ContentEntityBase {}
An entity-level constraint validator receives the entity object and could look like the following:
class EntityExampleConstraint extends Constraint {
public $message = 'The entity is not a valid example.';
}
class ExampleConstraintValidator extends ConstraintValidator {
/**
* {@inheritdoc}
*/
public function validate($entity, Constraint $constraint) {
if (isset($entity)) {
/** @var \Drupal\Core\Entity\EntityInterface $entity */
if (!$entity->isNew()) {
$this->context->addViolation($constraint->message);
}
}
}
}