Drupal 8: A custom validator for checking the field depending on the value of another field

23 Mar.2018
A custom validator for checking the field depending on the value of another field

In one project I use two independent date fields "Start Date" field_start_date and "End Date" field_end_date (with a Calendar). Both fields are optional. But if "End Date" is selected, I need to check "Start Date", so the first field shouldn't be empty and "End Date" should be greater or equal.

This code shows how I implemented the solution, which checks a few fields in the same time.

File
[MODULE_NAME]/[MODULE_NAME].module
<?php
 
use Drupal\Core\Entity\EntityTypeInterface;
 
/**
 * Implements hook_entity_bundle_field_info_alter().
 */
function [MODULE_NAME]_entity_bundle_field_info_alter(&$fields, EntityTypeInterface $entity_type, $bundle) {
  if (!empty($fields['field_end_date'])) {
    $fields['field_end_date']->addConstraint('StartEndDateChecking');
  }
}
File
[MODULE_NAME]/src/Plugin/Validation/Constraint/StartEndDateCheckingConstraint.php
<?php
 
namespace Drupal\[MODULE_NAME]\Plugin\Validation\Constraint;
 
use Symfony\Component\Validator\Constraint;
 
/**
 * Checks if "End Date" not empty.
 *
 * "Start Date" must be not empty
 * and "End Date" must be greater then "Start Date".
 *
 * @Constraint(
 *   id = "StartEndDateChecking",
 *   label = @Translation("Start/End Date checking", context = "Validation")
 * )
 */
class StartEndDateCheckingConstraint extends Constraint {
 
  public $message = '"End Date" may not be less than "Start Date".';
 
}
File
[MODULE_NAME]/src/Plugin/Validation/Constraint/StartEndDateCheckingConstraintValidator.php
<?php
 
namespace Drupal\[MODULE_NAME]\Plugin\Validation\Constraint;
 
use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\ConstraintValidator;
use Drupal\Core\Datetime\DrupalDateTime;
 
/**
 * Validates start/end date.
 */
class StartEndDateCheckingConstraintValidator extends ConstraintValidator {
 
  /**
   * {@inheritdoc}
   */
  public function validate($entity, Constraint $constraint) {
 
    $entity = $entity->getEntity();
    $start_date = $entity->get('field_start_date')->getString();
    $end_date = $entity->get('field_end_date')->getString();
    if ($end_date == '') {
      return;
    }
 
    $tz = drupal_get_user_timezone();
    $start_date = new DrupalDateTime($start_date, $tz);
    $end_date = new DrupalDateTime($end_date, $tz);
 
    if ($start_date > $end_date) {
      $this->context->addViolation($constraint->message);
    }
  }
 
}