From 3d8a748c0c46c5fb75f597299c9538bc04f22f00 Mon Sep 17 00:00:00 2001 From: Sascha Grossenbacher <5019-berdir@users.noreply.drupalcode.org> Date: Tue, 6 Aug 2024 19:24:49 +0000 Subject: [PATCH] Issue #3027525 by Berdir, marcoscano, robin.ingelbrecht, nkamala, jsst, Maico de Jong, NikolaAt, kala4ek, miro_dietiker: Show the paragraph and field label of fields that fail validation --- .../Field/FieldWidget/ParagraphsWidget.php | 89 ++++++++++++++++++- .../ParagraphsAdministrationTest.php | 2 +- 2 files changed, 88 insertions(+), 3 deletions(-) diff --git a/src/Plugin/Field/FieldWidget/ParagraphsWidget.php b/src/Plugin/Field/FieldWidget/ParagraphsWidget.php index 2e061f76..2333b040 100644 --- a/src/Plugin/Field/FieldWidget/ParagraphsWidget.php +++ b/src/Plugin/Field/FieldWidget/ParagraphsWidget.php @@ -6,6 +6,7 @@ use Drupal\Component\Utility\NestedArray; use Drupal\Component\Utility\Html; use Drupal\Core\Entity\ContentEntityInterface; use Drupal\Core\Entity\Entity\EntityFormDisplay; +use Drupal\Core\Entity\EntityFieldManagerInterface; use Drupal\Core\Entity\FieldableEntityInterface; use Drupal\Core\Field\FieldDefinitionInterface; use Drupal\Core\Field\FieldFilteredMarkup; @@ -18,8 +19,10 @@ use Drupal\Core\Render\Element; use Drupal\Core\TypedData\TranslationStatusInterface; use Drupal\field_group\FormatterHelper; use Drupal\paragraphs\Entity\Paragraph; +use Drupal\paragraphs\Entity\ParagraphsType; use Drupal\paragraphs\ParagraphInterface; use Drupal\paragraphs\Plugin\EntityReferenceSelection\ParagraphSelection; +use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\Validator\ConstraintViolationInterface; use Symfony\Component\Validator\ConstraintViolationListInterface; @@ -94,6 +97,13 @@ class ParagraphsWidget extends WidgetBase { */ protected $accessOptions = NULL; + /** + * The entity field manager service. + * + * @var \Drupal\Core\Entity\EntityFieldManagerInterface + */ + protected $entityFieldManager; + /** * Constructs a ParagraphsWidget object. * @@ -107,17 +117,35 @@ class ParagraphsWidget extends WidgetBase { * The widget settings. * @param array $third_party_settings * Any third party settings. + * @param \Drupal\Core\Entity\EntityFieldManagerInterface $entity_field_manager + * The entity field manager service. */ - public function __construct($plugin_id, $plugin_definition, FieldDefinitionInterface $field_definition, array $settings, array $third_party_settings) { + public function __construct($plugin_id, $plugin_definition, FieldDefinitionInterface $field_definition, array $settings, array $third_party_settings, EntityFieldManagerInterface $entity_field_manager) { // Modify settings that were set before https://www.drupal.org/node/2896115. if(isset($settings['edit_mode']) && $settings['edit_mode'] === 'preview') { $settings['edit_mode'] = 'closed'; $settings['closed_mode'] = 'preview'; } + $this->entityFieldManager = $entity_field_manager; + parent::__construct($plugin_id, $plugin_definition, $field_definition, $settings, $third_party_settings); } + /** + * {@inheritdoc} + */ + public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) { + return new static( + $plugin_id, + $plugin_definition, + $configuration['field_definition'], + $configuration['settings'], + $configuration['third_party_settings'], + $container->get('entity_field.manager'), + ); + } + /** * {@inheritdoc} */ @@ -2653,7 +2681,7 @@ class ParagraphsWidget extends WidgetBase { continue; } - $form_state->setError($element[$item['_original_delta']], $this->t('Validation error on collapsed paragraph @path: @message', ['@path' => $violation->getPropertyPath(), '@message' => $violation->getMessage()])); + $this->massageCollapsedParagraphsErrorMessages($form, $form_state, $element, $item, $violation); } } } @@ -3107,4 +3135,61 @@ class ParagraphsWidget extends WidgetBase { return FALSE; } + /** + * Improve validation error messages by including additional labels. + * + * This will convert an error message of type: + * "Text field is required." + * into something like: + * "Error in field Content #1 (Hero): Text field is required." + * where "Content" is the top-level paragraph field label, "Hero" is the + * paragraph type label, and "1" is the position of the paragraph in the + * field (delta + 1). + * + * @param array $form + * The form array. + * @param \Drupal\Core\Form\FormStateInterface $form_state + * The form state object. + * @param array $element + * Element with error. + * @param array $item + * Sequence of the item. + * @param \Symfony\Component\Validator\ConstraintViolationInterface $violation + * Violation of the field. + */ + protected function massageCollapsedParagraphsErrorMessages(array $form, FormStateInterface $form_state, $element, $item, $violation) { + $field_name = $this->fieldDefinition->getName(); + $field_label = $form[$field_name]['widget']['#title'] ?? FALSE; + $delta = $item['_original_delta']; + $paragraph_type = $form[$field_name]['widget'][$delta]['#paragraph_type'] ?? FALSE; + if ($field_label && $paragraph_type) { + $paragraph_type_label = ParagraphsType::load($paragraph_type)->label(); + $property_path = $violation->getPropertyPath(); + $property_path = explode('.', $property_path)[0]; + + $fields = $this->entityFieldManager->getFieldDefinitions('paragraph', $paragraph_type); + if (isset($fields[$property_path])) { + $new_message = $this->t('Error in field %field #@position (@bundle), %subfield : @message', [ + '%field' => $field_label, + '@position' => $delta + 1, + '@bundle' => $paragraph_type_label, + '%subfield' => $fields[$property_path]->getLabel(), + '@message' => $violation->getMessage(), + ]); + } + else { + $new_message = $this->t('Error in field %field #@position (@bundle): @message', [ + '%field' => $field_label, + '@position' => $delta + 1, + '@bundle' => $paragraph_type_label, + '@message' => $violation->getMessage(), + ]); + } + + $form_state->setError($element[$item['_original_delta']], $new_message); + } else { + $form_state->setError($element[$item['_original_delta']], $this->t('Validation error on collapsed paragraph @path: @message', ['@path' => $violation->getPropertyPath(), '@message' => $violation->getMessage()])); + } + } + } diff --git a/tests/src/Functional/WidgetStable/ParagraphsAdministrationTest.php b/tests/src/Functional/WidgetStable/ParagraphsAdministrationTest.php index eb278025..139b3732 100644 --- a/tests/src/Functional/WidgetStable/ParagraphsAdministrationTest.php +++ b/tests/src/Functional/WidgetStable/ParagraphsAdministrationTest.php @@ -489,7 +489,7 @@ class ParagraphsAdministrationTest extends ParagraphsTestBase { $this->assertSession()->pageTextNotContains('The referenced entity (node: ' . $node->id() . ') does not exist.'); $this->assertSession()->fieldNotExists('field_paragraphs[1][subform][field_entity_reference][0][target_id]'); $this->submitForm([], 'Save'); - $this->assertSession()->pageTextContains('Validation error on collapsed paragraph field_entity_reference.0.target_id: The referenced entity (node: ' . $node->id() . ') does not exist.'); + $this->assertSession()->pageTextContains('Error in field field_paragraphs #1 (node_test), Entity reference : The referenced entity (node: ' . $node->id() . ') does not exist.'); // Attempt to edit the Paragraph. $this->submitForm([], 'field_paragraphs_0_edit'); -- GitLab