diff --git a/core/lib/Drupal/Core/Validation/Plugin/Validation/Constraint/UniqueFieldValueValidator.php b/core/lib/Drupal/Core/Validation/Plugin/Validation/Constraint/UniqueFieldValueValidator.php index 050f108d69ea4a1f75e0f02a0acac805745575e5..111bda03f228b166e99c81ef6ee945d4bb3aca0c 100644 --- a/core/lib/Drupal/Core/Validation/Plugin/Validation/Constraint/UniqueFieldValueValidator.php +++ b/core/lib/Drupal/Core/Validation/Plugin/Validation/Constraint/UniqueFieldValueValidator.php @@ -82,7 +82,7 @@ public function validate($items, Constraint $constraint) { // 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 = array_intersect($item_values, $other_entity_values); + $duplicate_values = $this->caseInsensitiveArrayIntersect($item_values, $other_entity_values); foreach ($duplicate_values as $delta => $dupe) { $violation = $this->context @@ -112,6 +112,26 @@ public function validate($items, Constraint $constraint) { } } + /** + * Perform a case-insensitive array intersection, but keep original capitalization. + * + * @param array $orig_values + * The original values to be returned. + * @param array $comp_values + * The values to intersect $orig_values with. + * + * @return array + * Elements of $orig_values contained in $comp_values when ignoring capitalization. + */ + private function caseInsensitiveArrayIntersect(array $orig_values, array $comp_values): array { + $lowercase_comp_values = array_map('strtolower', $comp_values); + $intersect_map = array_map(fn (string $x) => in_array(strtolower($x), $lowercase_comp_values, TRUE) ? $x : NULL, $orig_values); + + return array_filter($intersect_map, function ($x) { + return $x !== NULL; + }); + } + /** * Get an array of duplicate field values. * diff --git a/core/tests/Drupal/KernelTests/Core/Validation/UniqueValuesConstraintValidatorTest.php b/core/tests/Drupal/KernelTests/Core/Validation/UniqueValuesConstraintValidatorTest.php index 7bfd326bc204cd0afacb64d9359fb0ca0ffb5053..dd8f0d3eff2ae05db3e96c9372979cb690a7d413 100644 --- a/core/tests/Drupal/KernelTests/Core/Validation/UniqueValuesConstraintValidatorTest.php +++ b/core/tests/Drupal/KernelTests/Core/Validation/UniqueValuesConstraintValidatorTest.php @@ -288,4 +288,42 @@ public function testValidationMultiple() { } + /** + * Tests the UniqueField validation constraint validator with regards to case-insensitivity. + * + * Case 5. Try to create another entity with existing value for unique field with different capitalization. + * + * @throws \Drupal\Core\Entity\EntityStorageException + * + * @covers ::validate + */ + public function testValidationCaseInsensitive(): void { + // Create entity with two values for the testing field. + $definition = [ + 'id' => (int) rand(0, getrandmax()), + 'user_id' => 0, + 'field_test_text' => [ + 'text1', + 'text2', + ], + ]; + $entity = EntityTestUniqueConstraint::create($definition); + $entity->save(); + + // Create another entity with two values for the testing field, one identical + // to other value, but with different capitalization which should still trigger a validation error. + $definition = [ + 'id' => (int) rand(0, getrandmax()), + 'user_id' => 0, + 'field_test_text' => [ + 'Text1', + 'text3', + ], + ]; + $entity = EntityTestUniqueConstraint::create($definition); + $violations = $entity->validate(); + $this->assertCount(1, $violations); + $this->assertEquals('field_test_text.0', $violations[0]->getPropertyPath()); + } + }