Problem description

When a field contains an invalid reference, the message states

There are no entities matching "invalid reference"

This is a UI/UX issue for the following reasons:

- Normal users (i.e. non-developers) have absolutely no idea what an "entity" is, and therefore this message is meaningless to them
- It does not state which field is causing the error

entity reference error message

Proposed resolution

Error messages should be easy to understand and act upon.
In the above case the user-facing messages should be something like:

Invalid value(s) (invalid value 1, invalid value 2, ...) chosen for field_name_1.
Invalid value(s) (invalid value 4, invalid value 5, ...) chosen for field_name_2.

If we wanted to be even extra user-friendly, we should also specify the type of the field (and allow corresponding setting in the entity configuration page), similar to how it's possible to change the text of the Entity Browser buttons, so the message can say:

Invalid term (invalid value 1, invalid value 2, ...) chosen for field_name_1.
Invalid Category (invalid value 4, invalid value 5, ...) chosen for field_name_2.
Invalid Post (invalid value 4, invalid value 5, ...) chosen for field_name_3.

etc.

As it was pointed by @alexpott in #34, we can do the following:

One thing we can do to improve this message is use the entity type label information. We'll need to get the type from the entity type manager. I think \Drupal::entityTypeManager()->getDefinition($element['#target_type'])->getPluralLabel() might work.

User interface changes

Entity reference validation messages will contain entity type plural label instead of "entities".
before
after

Issue fork drupal-3138631

Command icon Show commands

Start within a Git clone of the project using the version control instructions.

Or, if you do not have SSH keys set up on git.drupalcode.org:

Support from Acquia helps fund testing for Drupal Acquia logo

Comments

z.stolar created an issue. See original summary.

z.stolar’s picture

Issue summary: View changes
snehalgaikwad’s picture

Status: Active » Needs review
FileSize
823 bytes
29 KB

Fixed the error message, added field name with the wrong value for more clarity.

Status: Needs review » Needs work

The last submitted patch, 3: 3138631-3.patch, failed testing. View results

pavnish’s picture

Assigned: Unassigned » pavnish
snehalgaikwad’s picture

Assigned: pavnish » Unassigned
Status: Needs work » Needs review
FileSize
1.7 KB

Fixed failed test case.

Status: Needs review » Needs work

The last submitted patch, 6: 3138631-6.patch, failed testing. View results

snehalgaikwad’s picture

Assigned: Unassigned » snehalgaikwad
snehalgaikwad’s picture

Assigned: snehalgaikwad » Unassigned
Status: Needs work » Needs review
FileSize
4.09 KB

I tried to change the error message 'There are no entities matching "invalid reference"' to 'Invalid value "value" chosen for "field_name"'. I made a change in the EntityAutocomplete file by adding the name of the field entity reference. In method 'matchEntityByTitle' $element is a form element i.e entity reference field. So I used it's title attribute to get the label of the field.
There are 4 test cases that need to be updated. While fixing test cases I realized there can be entity reference field without the label. In one of the cases, entity reference field is used as an exposed filter without a label so test case failed. I could think of one solution as if a title is available then I used a new error message with a field name in it and if not then using the previous error. What can be the better solution for this case when the title for the field is not available? Also, will it be good if I try using the machine name of the field instead of title? Please suggest.

z.stolar’s picture

Thank you @snehalgaikwad for your work!

IMO, when a label is not available on exposed filters or a field, it usually still has a label in the field's configuration. You should use this one and fall back to machine name if necessary.

Does that respond to your question?

snehalgaikwad’s picture

Hi @z.stolar, I tried finding a label for field but couldn't get. In matchEntityByTitle() method in EntityAutocpmplete file, element is just an array. Couldn't find anything related to a label. Here attaching element variable, both with title and without title.
With title:

array (size=44)
  '#type' => string 'entity_autocomplete' (length=19)
  '#title' => 
    object(Drupal\Core\StringTranslation\TranslatableMarkup)[1192]
      protected 'translatedMarkup' => null
      protected 'options' => 
        array (size=0)
          empty
      protected 'stringTranslation' => 
        object(Drupal\Core\StringTranslation\TranslationManager)[609]
          protected 'translators' => 
            array (size=1)
              ...
          protected 'sortedTranslators' => 
            array (size=1)
              ...
          protected 'defaultLangcode' => string 'en' (length=2)
          public '_serviceId' => string 'string_translation' (length=18)
      protected 'string' => string 'Usernames' (length=9)
      protected 'arguments' => 
        array (size=0)
          empty
  '#description' => 
    object(Drupal\Core\StringTranslation\TranslatableMarkup)[1193]
      protected 'translatedMarkup' => null
      protected 'options' => 
        array (size=0)
          empty
      protected 'stringTranslation' => 
        object(Drupal\Core\StringTranslation\TranslationManager)[609]
          protected 'translators' => 
            array (size=1)
              ...
          protected 'sortedTranslators' => 
            array (size=1)
              ...
          protected 'defaultLangcode' => string 'en' (length=2)
          public '_serviceId' => string 'string_translation' (length=18)
      protected 'string' => string 'Enter a comma separated list of user names.' (length=43)
      protected 'arguments' => 
        array (size=0)
          empty
  '#target_type' => string 'user' (length=4)
  '#tags' => boolean true
  '#default_value' => string '' (length=0)
  '#process_default_value' => boolean true
  '#prefix' => string '<div class="views-group-box views-right-60">' (length=44)
  '#suffix' => string '</div>' (length=6)
  '#input' => boolean true
  '#size' => int 60
  '#maxlength' => int 128
  '#autocomplete_route_name' => string 'system.entity_autocomplete' (length=26)
  '#process' => 
    array (size=5)
      0 => 
        array (size=2)
          0 => string 'Drupal\Core\Entity\Element\EntityAutocomplete' (length=45)
          1 => string 'processEntityAutocomplete' (length=25)
      1 => 
        array (size=2)
          0 => string 'Drupal\Core\Entity\Element\EntityAutocomplete' (length=45)
          1 => string 'processAutocomplete' (length=19)
      2 => 
        array (size=2)
          0 => string 'Drupal\Core\Entity\Element\EntityAutocomplete' (length=45)
          1 => string 'processAjaxForm' (length=15)
      3 => 
        array (size=2)
          0 => string 'Drupal\Core\Entity\Element\EntityAutocomplete' (length=45)
          1 => string 'processPattern' (length=14)
      4 => 
        array (size=2)
          0 => string 'Drupal\Core\Entity\Element\EntityAutocomplete' (length=45)
          1 => string 'processGroup' (length=12)
  '#pre_render' => 
    array (size=2)
      0 => 
        array (size=2)
          0 => string 'Drupal\Core\Entity\Element\EntityAutocomplete' (length=45)
          1 => string 'preRenderTextfield' (length=18)
      1 => 
        array (size=2)
          0 => string 'Drupal\Core\Entity\Element\EntityAutocomplete' (length=45)
          1 => string 'preRenderGroup' (length=14)
  '#theme' => string 'input__textfield' (length=16)
  '#theme_wrappers' => 
    array (size=1)
      0 => string 'form_element' (length=12)
  '#selection_handler' => string 'default' (length=7)
  '#selection_settings' => 
    array (size=0)
      empty
  '#autocreate' => null
  '#validate_reference' => boolean true
  '#element_validate' => 
    array (size=1)
      0 => 
        array (size=2)
          0 => string 'Drupal\Core\Entity\Element\EntityAutocomplete' (length=45)
          1 => string 'validateEntityAutocomplete' (length=26)
  '#value_callback' => 
    array (size=2)
      0 => string 'Drupal\Core\Entity\Element\EntityAutocomplete' (length=45)
      1 => string 'valueCallback' (length=13)
  '#defaults_loaded' => boolean true
  '#tree' => boolean true
  '#parents' => 
    array (size=2)
      0 => string 'options' (length=7)
      1 => string 'value' (length=5)
  '#array_parents' => 
    array (size=2)
      0 => string 'options' (length=7)
      1 => string 'value' (length=5)
  '#weight' => float 0.009
  '#processed' => boolean true
  '#required' => boolean false
  '#attributes' => 
    array (size=4)
      'data-drupal-selector' => string 'edit-options-value' (length=18)
      'aria-describedby' => string 'edit-options-value--description' (length=31)
      'class' => 
        array (size=1)
          0 => string 'form-autocomplete' (length=17)
      'data-autocomplete-path' => string '/entity_reference_autocomplete/user/default/OnXbnUTlSESRFkxZ9HpZlH1Qb64nnI0UJFb8xPnOSC0' (length=87)
  '#title_display' => string 'before' (length=6)
  '#description_display' => string 'after' (length=5)
  '#errors' => null
  '#id' => string 'edit-options-value' (length=18)
  '#name' => string 'options[value]' (length=14)
  '#value' => string 'v2zvloui' (length=8)
  '#needs_validation' => boolean true
  '#autocomplete_route_parameters' => 
    array (size=3)
      'target_type' => string 'user' (length=4)
      'selection_handler' => string 'default' (length=7)
      'selection_settings_key' => string 'OnXbnUTlSESRFkxZ9HpZlH1Qb64nnI0UJFb8xPnOSC0' (length=43)
  '#cache' => 
    array (size=3)
      'contexts' => 
        array (size=0)
          empty
      'tags' => 
        array (size=0)
          empty
      'max-age' => int -1
  '#attached' => 
    array (size=1)
      'library' => 
        array (size=1)
          0 => string 'core/drupal.autocomplete' (length=24)
  '#ajax_processed' => boolean false
  '#groups' => 
    array (size=16)
      'options][admin_label' => 
        array (size=1)
          '#group_exists' => boolean true
      'options][more' => 
        array (size=1)
          '#group_exists' => boolean true
      'options][expose_button][checkbox][checkbox' => 
        array (size=1)
          '#group_exists' => boolean true
      'options][value' => 
        array (size=1)
          '#group_exists' => boolean true
      'options][expose][required' => 
        array (size=1)
          '#group_exists' => boolean true
      'options][expose][label' => 
        array (size=1)
          '#group_exists' => boolean true
      'options][expose][description' => 
        array (size=1)
          '#group_exists' => boolean true
      'options][expose][use_operator' => 
        array (size=1)
          '#group_exists' => boolean true
      'options][expose][operator_limit_selection' => 
        array (size=1)
          '#group_exists' => boolean true
      'options][expose][operator_id' => 
        array (size=1)
          '#group_exists' => boolean true
      'options][expose][remember' => 
        array (size=1)
          '#group_exists' => boolean true
      'options][expose][remember_roles][anonymous' => 
        array (size=1)
          '#group_exists' => boolean true
      'options][expose][remember_roles][authenticated' => 
        array (size=1)
          '#group_exists' => boolean true
      'options][expose][remember_roles][zfqpx43n' => 
        array (size=1)
          '#group_exists' => boolean true
      'options][expose][identifier' => 
        array (size=1)
          '#group_exists' => boolean true
      'options][expose][reduce' => 
        array (size=1)
          '#group_exists' => boolean true
  '#sorted' => boolean true

Without title

array (size=41)
  '#type' => string 'entity_autocomplete' (length=19)
  '#description' => 
    object(Drupal\Core\StringTranslation\TranslatableMarkup)[3006]
      protected 'translatedMarkup' => null
      protected 'options' => 
        array (size=0)
          empty
      protected 'stringTranslation' => 
        object(Drupal\Core\StringTranslation\TranslationManager)[611]
          protected 'translators' => 
            array (size=1)
              ...
          protected 'sortedTranslators' => 
            array (size=1)
              ...
          protected 'defaultLangcode' => string 'en' (length=2)
          public '_serviceId' => string 'string_translation' (length=18)
      protected 'string' => string 'Enter a comma separated list of user names.' (length=43)
      protected 'arguments' => 
        array (size=0)
          empty
  '#target_type' => string 'user' (length=4)
  '#tags' => boolean true
  '#default_value' => string '' (length=0)
  '#process_default_value' => boolean true
  '#input' => boolean true
  '#size' => int 60
  '#maxlength' => int 128
  '#autocomplete_route_name' => string 'system.entity_autocomplete' (length=26)
  '#process' => 
    array (size=5)
      0 => 
        array (size=2)
          0 => string 'Drupal\Core\Entity\Element\EntityAutocomplete' (length=45)
          1 => string 'processEntityAutocomplete' (length=25)
      1 => 
        array (size=2)
          0 => string 'Drupal\Core\Entity\Element\EntityAutocomplete' (length=45)
          1 => string 'processAutocomplete' (length=19)
      2 => 
        array (size=2)
          0 => string 'Drupal\Core\Entity\Element\EntityAutocomplete' (length=45)
          1 => string 'processAjaxForm' (length=15)
      3 => 
        array (size=2)
          0 => string 'Drupal\Core\Entity\Element\EntityAutocomplete' (length=45)
          1 => string 'processPattern' (length=14)
      4 => 
        array (size=2)
          0 => string 'Drupal\Core\Entity\Element\EntityAutocomplete' (length=45)
          1 => string 'processGroup' (length=12)
  '#pre_render' => 
    array (size=2)
      0 => 
        array (size=2)
          0 => string 'Drupal\Core\Entity\Element\EntityAutocomplete' (length=45)
          1 => string 'preRenderTextfield' (length=18)
      1 => 
        array (size=2)
          0 => string 'Drupal\Core\Entity\Element\EntityAutocomplete' (length=45)
          1 => string 'preRenderGroup' (length=14)
  '#theme' => string 'input__textfield' (length=16)
  '#theme_wrappers' => 
    array (size=1)
      0 => string 'form_element' (length=12)
  '#selection_handler' => string 'default' (length=7)
  '#selection_settings' => 
    array (size=0)
      empty
  '#autocreate' => null
  '#validate_reference' => boolean true
  '#element_validate' => 
    array (size=1)
      0 => 
        array (size=2)
          0 => string 'Drupal\Core\Entity\Element\EntityAutocomplete' (length=45)
          1 => string 'validateEntityAutocomplete' (length=26)
  '#value_callback' => 
    array (size=2)
      0 => string 'Drupal\Core\Entity\Element\EntityAutocomplete' (length=45)
      1 => string 'valueCallback' (length=13)
  '#defaults_loaded' => boolean true
  '#tree' => boolean false
  '#parents' => 
    array (size=1)
      0 => string 'uid' (length=3)
  '#array_parents' => 
    array (size=1)
      0 => string 'uid' (length=3)
  '#weight' => int 0
  '#processed' => boolean true
  '#required' => boolean false
  '#attributes' => 
    array (size=4)
      'data-drupal-selector' => string 'edit-uid' (length=8)
      'aria-describedby' => string 'edit-uid--description' (length=21)
      'class' => 
        array (size=1)
          0 => string 'form-autocomplete' (length=17)
      'data-autocomplete-path' => string '/entity_reference_autocomplete/user/default/-LmIlVCwMAybEDzB-KNeV8IxWG5pRW61Ou9Kd06flNg' (length=87)
  '#title_display' => string 'before' (length=6)
  '#description_display' => string 'after' (length=5)
  '#errors' => null
  '#id' => string 'edit-uid' (length=8)
  '#name' => string 'uid' (length=3)
  '#value' => string 'oolgthqt' (length=8)
  '#needs_validation' => boolean true
  '#autocomplete_route_parameters' => 
    array (size=3)
      'target_type' => string 'user' (length=4)
      'selection_handler' => string 'default' (length=7)
      'selection_settings_key' => string '-LmIlVCwMAybEDzB-KNeV8IxWG5pRW61Ou9Kd06flNg' (length=43)
  '#cache' => 
    array (size=3)
      'contexts' => 
        array (size=0)
          empty
      'tags' => 
        array (size=0)
          empty
      'max-age' => int -1
  '#attached' => 
    array (size=1)
      'library' => 
        array (size=1)
          0 => string 'core/drupal.autocomplete' (length=24)
  '#ajax_processed' => boolean false
  '#groups' => 
    array (size=1)
      'uid' => 
        array (size=1)
          '#group_exists' => boolean true
  '#sorted' => boolean true
z.stolar’s picture

Patch at #9 works very well for me.

z.stolar’s picture

z.stolar’s picture

Status: Needs review » Reviewed & tested by the community
alexpott’s picture

Status: Reviewed & tested by the community » Needs work
+++ b/core/lib/Drupal/Core/Entity/Element/EntityAutocomplete.php
@@ -281,7 +281,12 @@ protected static function matchEntityByTitle(SelectionInterface $handler, $input
-        $form_state->setError($element, t('There are no entities matching "%value".', $params));
+        if(isset($element['#title'])){
+          $form_state->setError($element, t('Invalid value "%value" chosen for "%field_name".', $params + ['%field_name' => $element['#title']]));
+        }
+        else{
+          $form_state->setError($element, t('There are no entities matching "%value".', $params));
+        }

I don't think we should be including the field name in the error text - this would be incorrect for inline form errors module - for example. And also we don't do this for other form errors.

One thing we can do to improve this message is use the entity type label information. We'll need to get the type from the entity type manager. I think \Drupal::entityTypeManager()->getDefinition($element['#target_type'])->getPluralLabel() might work.

pavnish’s picture

@alexpott I have checked #9 with "Inline Form Errors" module it's working fine.

shetpooja04’s picture

I am working on it

shetpooja04’s picture

Status: Needs work » Needs review
FileSize
4.21 KB
1.12 KB

Uploaded patch as per the suggestion in #15, Please review

shetpooja04’s picture

Issue tags: +DIACWAug2020

Status: Needs review » Needs work

The last submitted patch, 18: 3138631-18.patch, failed testing. View results
- codesniffer_fixes.patch Interdiff of automated coding standards fixes only.

snehalgaikwad’s picture

Assigned: Unassigned » snehalgaikwad
Berdir’s picture

+++ b/core/lib/Drupal/Core/Entity/Element/EntityAutocomplete.php
@@ -327,7 +327,13 @@ protected static function matchEntityByTitle(SelectionInterface $handler, $input
     if (empty($entities)) {
       if ($strict) {
         // Error if there are no entities available for a required field.
-        $form_state->setError($element, t('There are no entities matching "%value".', $params));
+        $entity_type_label = \Drupal::entityTypeManager()->getDefinition($element['#target_type'])->getPluralLabel();
+        if (isset($entity_type_label)) {
+          $form_state->setError($element, t('Invalid value "%value" chosen for "%field_name".', $params + ['%field_name' => $entity_type_label]));
+        }
+        else{
+          $form_state->setError($element, t('There are no entities matching "%value".', $params));
+        }

placeholder is still field name now which doesn't make sense and the tests need to be updated to match what we display now.

Matroskeen’s picture

Status: Needs work » Needs review
FileSize
8.85 KB
9.37 KB

1) Applied changes according to #15 and adjusted tests as was pointed in #22;
2) Also, added labels for other types of validation messages in EntityAutocomplete.php;

I didn't find test for the following message:
"Many @entities are called %value. Specify the one you want by appending the id in parentheses, like "@value (@id)"."

Should we add a test coverage for this particular case?

snehalgaikwad’s picture

Assigned: snehalgaikwad » Unassigned
Matroskeen’s picture

Title: Entity reference error messages are not user friendly » Improve Entity reference validation error messages
Issue summary: View changes
FileSize
21.81 KB
24.93 KB

Updated the issue summary and title.
Is there anything else we can do to move the issue forward?

amateescu’s picture

+++ b/core/lib/Drupal/Core/Entity/Element/EntityAutocomplete.php
@@ -323,17 +323,18 @@ protected static function matchEntityByTitle(SelectionInterface $handler, $input
+      '@entities' => \Drupal::entityTypeManager()->getDefinition($element['#target_type'])->getPluralLabel(),

Can we rename the @entities parameter to @entity_type_plural for more clarity?

Other than that, the patch looks really good to me :)

Matroskeen’s picture

Thanks, here is the updated patch.

Matroskeen’s picture

Version: 8.9.x-dev » 9.2.x-dev
Matroskeen’s picture

Title: Improve Entity reference validation error messages » Improve entity reference validation error messages
Component: entity_reference.module » entity system

Changing the component, because there are no changes in entity_reference module.

amateescu’s picture

Status: Needs review » Reviewed & tested by the community

Looks ready to go!

z.stolar’s picture

Thank you everyone who participated in getting this one through!

catch’s picture

Status: Reviewed & tested by the community » Needs work
Issue tags: +Needs reroll

This looks great, but needs a re-roll.

Matroskeen’s picture

Assigned: Unassigned » Matroskeen

Will open merge request soon.

Matroskeen’s picture

Assigned: Matroskeen » Unassigned
Status: Needs work » Reviewed & tested by the community

The merge request with re-roll is open.
Moving back to RTBC as per #31.

  • catch committed f0a40c9 on 9.2.x
    Issue #3138631 by Matroskeen, snehalgaikwad, shetpooja04, z.stolar,...
catch’s picture

Status: Reviewed & tested by the community » Fixed

Committed f0a40c9 and pushed to 9.2.x. Thanks!

Status: Fixed » Closed (fixed)

Automatically closed - issue fixed for 2 weeks with no activity.

jibran’s picture

Issue tags: -Needs reroll +DER issue

Updating the tags as it broke DER HEAD as well.