function UniqueFieldValueValidator::validate

Same name in other branches
  1. 9 core/lib/Drupal/Core/Validation/Plugin/Validation/Constraint/UniqueFieldValueValidator.php \Drupal\Core\Validation\Plugin\Validation\Constraint\UniqueFieldValueValidator::validate()
  2. 8.9.x core/lib/Drupal/Core/Validation/Plugin/Validation/Constraint/UniqueFieldValueValidator.php \Drupal\Core\Validation\Plugin\Validation\Constraint\UniqueFieldValueValidator::validate()
  3. 11.x core/lib/Drupal/Core/Validation/Plugin/Validation/Constraint/UniqueFieldValueValidator.php \Drupal\Core\Validation\Plugin\Validation\Constraint\UniqueFieldValueValidator::validate()

File

core/lib/Drupal/Core/Validation/Plugin/Validation/Constraint/UniqueFieldValueValidator.php, line 41

Class

UniqueFieldValueValidator
Validates that a field is unique for the given entity type.

Namespace

Drupal\Core\Validation\Plugin\Validation\Constraint

Code

public function validate($items, Constraint $constraint) {
    if (!$items->first()) {
        return;
    }
    
    /** @var \Drupal\Core\Entity\EntityInterface $entity */
    $entity = $items->getEntity();
    $entity_type = $entity->getEntityType();
    $entity_type_id = $entity_type->id();
    $entity_label = $entity->getEntityType()
        ->getSingularLabel();
    $field_name = $items->getFieldDefinition()
        ->getName();
    $field_label = $items->getFieldDefinition()
        ->getLabel();
    $field_storage_definitions = $this->entityFieldManager
        ->getFieldStorageDefinitions($entity_type_id);
    $property_name = $field_storage_definitions[$field_name]->getMainPropertyName();
    $id_key = $entity_type->getKey('id');
    $is_multiple = $field_storage_definitions[$field_name]->isMultiple();
    $is_new = $entity->isNew();
    $item_values = array_column($items->getValue(), $property_name);
    // Check if any item values for this field already exist in other entities.
    $query = $this->entityTypeManager
        ->getStorage($entity_type_id)
        ->getAggregateQuery()
        ->accessCheck(FALSE)
        ->groupBy("{$field_name}.{$property_name}");
    if (!$is_new) {
        $entity_id = $entity->id();
        $query->condition($id_key, $entity_id, '<>');
    }
    if ($constraint->caseSensitive) {
        $query->condition($field_name, $item_values, 'IN');
    }
    else {
        $or_group = $query->orConditionGroup();
        foreach ($item_values as $item_value) {
            $or_group->condition($field_name, \Drupal::database()->escapeLike($item_value), 'LIKE');
        }
        $query->condition($or_group);
    }
    $results = $query->execute();
    if (!empty($results)) {
        // The results array is a single-column multidimensional array. The
        // column key includes the field name but may or may not include the
        // property name. Pop the column key from the first result to be sure.
        $column_key = key(reset($results));
        $other_entity_values = array_column($results, $column_key);
        // If our entity duplicates field values in any other entity, the query
        // will return all field values that belong to those entities. Narrow
        // down to only the specific duplicate values.
        $duplicate_values = $this->caseInsensitiveArrayIntersect($item_values, $other_entity_values);
        foreach ($duplicate_values as $delta => $dupe) {
            $violation = $this->context
                ->buildViolation($constraint->message)
                ->setParameter('@entity_type', $entity_label)
                ->setParameter('@field_name', $field_label)
                ->setParameter('%value', $dupe);
            if ($is_multiple) {
                $violation->atPath((string) $delta);
            }
            $violation->addViolation();
        }
    }
    // Check if items are duplicated within this entity.
    if ($is_multiple) {
        $duplicate_values = $this->extractDuplicates($item_values);
        foreach ($duplicate_values as $delta => $dupe) {
            $this->context
                ->buildViolation($constraint->message)
                ->setParameter('@entity_type', $entity_label)
                ->setParameter('@field_name', $field_label)
                ->setParameter('%value', $dupe)
                ->atPath((string) $delta)
                ->addViolation();
        }
    }
}

Buggy or inaccurate documentation? Please file an issue. Need support? Need help programming? Connect with the Drupal community.