diff --git a/core/lib/Drupal/Core/Field/WidgetBase.php b/core/lib/Drupal/Core/Field/WidgetBase.php index b1579f2..bcdf17a 100644 --- a/core/lib/Drupal/Core/Field/WidgetBase.php +++ b/core/lib/Drupal/Core/Field/WidgetBase.php @@ -410,21 +410,26 @@ public function flagErrors(FieldItemListInterface $items, ConstraintViolationLis if (Element::isVisibleElement($element)) { $handles_multiple = $this->handlesMultipleValues(); - $violations_by_delta = []; + $violations_by_delta = $item_list_violations = []; foreach ($violations as $violation) { // Separate violations by delta. $property_path = explode('.', $violation->getPropertyPath()); $delta = array_shift($property_path); - $violations_by_delta[$delta][] = $violation; + if (is_numeric($delta)) { + $violations_by_delta[$delta][] = $violation; + } + // Violations at the ItemList level are not associated to any delta. + else { + $item_list_violations[] = $violation; + } $violation->arrayPropertyPath = $property_path; } /** @var \Symfony\Component\Validator\ConstraintViolationInterface[] $delta_violations */ foreach ($violations_by_delta as $delta => $delta_violations) { - // Pass violations to the main element: - // - if this is a multiple-value widget, - // - or if the violations are at the ItemList level. - if ($handles_multiple || !is_numeric($delta)) { + // Pass violations to the main element if this is a multiple-value + // widget. + if ($handles_multiple) { $delta_element = $element; } // Otherwise, pass errors by delta to the corresponding sub-element. @@ -440,6 +445,13 @@ public function flagErrors(FieldItemListInterface $items, ConstraintViolationLis } } } + + /** @var \Symfony\Component\Validator\ConstraintViolationInterface[] $item_list_violations */ + // Pass violations to the main element without going through + // errorElement() if the violations are at the ItemList level. + foreach ($item_list_violations as $violation) { + $form_state->setError($element, $violation->getMessage()); + } } } } diff --git a/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestConstraintViolation.php b/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestConstraintViolation.php index e12c74a..b604b2c 100644 --- a/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestConstraintViolation.php +++ b/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestConstraintViolation.php @@ -3,6 +3,7 @@ namespace Drupal\entity_test\Entity; use Drupal\Core\Entity\EntityTypeInterface; +use Drupal\Core\Field\BaseFieldDefinition; /** * Defines the test entity class for testing entity constraint violations. @@ -39,6 +40,16 @@ public static function baseFieldDefinitions(EntityTypeInterface $entity_type) { ]); $fields['name']->addConstraint('FieldWidgetConstraint', []); + // Add a field that uses a widget with a custom implementation for + // \Drupal\Core\Field\WidgetInterface::errorElement(). + $fields['test_field'] = BaseFieldDefinition::create('test_field') + ->setLabel(t('Test field')) + ->setDisplayOptions('form', [ + 'type' => 'test_field_widget', + 'weight' => 1, + ]) + ->addConstraint('FieldWidgetConstraint', []); + return $fields; } diff --git a/core/tests/Drupal/KernelTests/Core/Entity/FieldWidgetConstraintValidatorTest.php b/core/tests/Drupal/KernelTests/Core/Entity/FieldWidgetConstraintValidatorTest.php index 041b062..21eb4d9 100644 --- a/core/tests/Drupal/KernelTests/Core/Entity/FieldWidgetConstraintValidatorTest.php +++ b/core/tests/Drupal/KernelTests/Core/Entity/FieldWidgetConstraintValidatorTest.php @@ -15,7 +15,7 @@ */ class FieldWidgetConstraintValidatorTest extends KernelTestBase { - public static $modules = ['entity_test', 'field', 'user', 'system']; + public static $modules = ['entity_test', 'field', 'field_test', 'user', 'system']; /** * {@inheritdoc} @@ -54,7 +54,8 @@ public function testValidation() { $display->validateFormValues($entity, $form, $form_state); $errors = $form_state->getErrors(); - $this->assertEqual($errors['name'], 'Widget constraint has failed.', 'Constraint violation is generated correctly'); + $this->assertEqual($errors['name'], 'Widget constraint has failed.', 'Constraint violation at the field items list level is generated correctly'); + $this->assertEqual($errors['test_field'], 'Widget constraint has failed.', 'Constraint violation at the field items list level is generated correctly for an advanced widget'); } /**