Problem/Motivation

It is possible to create an entity with label "0". However, this entity will be ignored by the entity autocomplete form element.

Steps to reproduce

Create a new term with name "0". Take any node type and configure a new field that refers to terms. Set any autocomplete widget for this field. Take any node of this type and open the edit form. Input "0" in the field - there are no autocomplete suggestions. After saving the node, the field will remain empty. However, if you input "0 " (trailing space), then the field value will be saved correctly.

Proposed resolution

Do not use empty() to check the form element value. Do not use the (bool) cast to check the typed string from the URL.

CommentFileSizeAuthor
#44 interdiff_3383131_34-40.txt1.09 KBankithashetty
#40 3383131-39.patch5.72 KBpradhumanjain2311
#34 interdiff27_34.txt1.5 KBroshni27
#34 3383131-34.patch5.75 KBroshni27
#30 entity-autocomplete-ignores-entities-3383131-27.patch5.7 KBxjm
#28 entity-autocomplete-ignores-entities-3383131-27-FAIL.patch3.88 KBashley_herodev
#27 interdiff_14-27.txt1000 bytesWalkingDexter
#27 entity-autocomplete-ignores-entities-3383131-27.patch5.7 KBWalkingDexter
#24 interdiff_14-24.txt979 bytesallisonherodevs
#24 entity-autocomplete-ignores-entities-3383131-24.patch5.28 KBallisonherodevs
#24 entity-autocomplete-ignores-entities-3383131-24-FAIL.patch3.46 KBallisonherodevs
#21 interdiff_14-21.txt971 bytesallisonherodevs
#17 interdiff_9-14.txt1.86 KBWalkingDexter
#14 entity-autocomplete-ignores-entities-3383131-14.patch5.05 KBWalkingDexter
#9 entity-autocomplete-ignores-entities-3383131-9.patch4.85 KBWalkingDexter
#5 3383131-nr-bot.txt120 bytesneeds-review-queue-bot
#3 taxonomy-autocomplete-ignores-terms-3383131-3.patch642 bytesWalkingDexter
#2 entity-autocomplete-ignores-entities-3383131-2.patch1.62 KBWalkingDexter

Issue fork drupal-3383131

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

WalkingDexter created an issue. See original summary.

WalkingDexter’s picture

Status: Active » Needs review
FileSize
1.62 KB
WalkingDexter’s picture

Drupal 7 also has this problem with taxonomy autocomplete.

WalkingDexter’s picture

Fix for terms without core patch (Drupal 7).

/**
 * Implements hook_field_widget_WIDGET_TYPE_form_alter().
 */
function custom_field_widget_taxonomy_autocomplete_form_alter(&$element, &$form_state, $context) {
  array_unshift($element['#element_validate'], 'custom_taxonomy_autocomplete_validate');
}

/**
 * Form element validate handler for taxonomy term autocomplete element.
 *
 * @param array $element
 *   The element structure.
 */
function custom_taxonomy_autocomplete_validate(array &$element) {
  // Fix for single term with name "0".
  if ($element['#value'] === '0') {
    $element['#value'] .= ' ';
  }
}
needs-review-queue-bot’s picture

Status: Needs review » Needs work
FileSize
120 bytes

The Needs Review Queue Bot tested this issue. It no longer applies to Drupal core. Therefore, this issue status is now "Needs work".

This does not mean that the patch needs to be re-rolled or the MR rebased. Read the Issue Summary, the issue tags and the latest discussion here to determine what needs to be done.

Consult the Drupal Contributor Guide to find step-by-step guides for working with issues.

WalkingDexter’s picture

Status: Needs work » Needs review
Issue tags: +no-needs-review-bot
nod_’s picture

Issue tags: -no-needs-review-bot

We can hide patches from the issue summary so that the testbot ignores them if they don't match the issue version.

smustgrave’s picture

Status: Needs review » Needs work
Issue tags: +Needs tests

Thank you for reporting.

Can we get a test case showing the issue please

WalkingDexter’s picture

Status: Needs work » Needs review
Issue tags: -Needs tests
FileSize
4.85 KB

Patch with tests.

smustgrave’s picture

Status: Needs review » Reviewed & tested by the community
Issue tags: +Bug Smash Initiative, +Needs Review Queue Initiative

Thanks for adding those! For next time super helpful to add a test-only patch with the full patch in that order. So we can see the red/green quickly. But not a big deal!

Ran the tests locally without the fix

testValidEntityAutocompleteElement:
Failed asserting that null matches expected '3'.
Expected :3
Actual :null

testEntityReferenceAutocompletion:
Undefined array key 0
/var/www/html/vendor/symfony/phpunit-bridge/DeprecationErrorHandler.php:104
/var/www/html/web/core/tests/Drupal/KernelTests/Core/Entity/EntityAutocompleteTest.php:137
/var/www/html/web/vendor/phpunit/phpunit/src/Framework/TestResult.php:728

So believe the tests are good.

Issue summary is clearly filled out so I think this is good for RTBC bucket.

Status: Reviewed & tested by the community » Needs work
smustgrave’s picture

Status: Needs work » Reviewed & tested by the community

Seems unrelated, retesting

xjm’s picture

Status: Reviewed & tested by the community » Needs work

OK, this bug is hilarious. Great find!

I'd be surprised if there hasn't already been an issue filed for this, but on the other hand I wasn't able to find one myself in a few minutes of searching. (It's harder because 0 is less than three characters and therefore ignored by d.o's issue search.)

  1. +++ b/core/lib/Drupal/Core/Entity/Element/EntityAutocomplete.php
    @@ -203,7 +203,7 @@ public static function processEntityAutocomplete(array &$element, FormStateInter
    -    if (!empty($element['#value'])) {
    +    if (isset($element['#value']) && $element['#value'] != '') {
    

    My first thought when opening this patch: What about other empty values besides 0 and ''? Let's think through them:

    • NULL is handled by the isset().
    • FALSE is not an expected value by default. However, the element (like all form data structures) is passed around by reference and alterable at every level by every module under the sun, so theoretically someone could have FALSE here for $weird_custom_code_reasons, in which case this would cause this code path to execute when it did not previously.
    • The above also goes for [] (which is also a totally conceivable thing for contrib to alter in since entity references can very logically be multivalue.
    • I'm unsure whether we should support ints and floats (0, 0.0), but it is a behavior change to start executing this code path for them when we didn't previously.

    Therefore, I think this should be changed to something along the lines of:

    if (isset($element['#value']) && ($element['#value'] === '0' || !empty($element['#value']))) {
    

    or perhaps:

    if (isset($element['#value']) && (is_numeric($element['#value']) || !empty($element['#value']))) {
    

    It should also have an inline comment, because it's non-obvious why we're doing this either way. Or, maybe we could add a method? Something like isNotEmpty() or whatever.

  2. +++ b/core/modules/system/src/Controller/EntityAutocompleteController.php
    @@ -78,7 +78,7 @@ public static function create(ContainerInterface $container) {
    -    if ($input = $request->query->get('q')) {
    +    if (($input = $request->query->get('q')) != '') {
    

    As above, this should probably be:

    $input = $request->query->get('q');
    if ($input === '0' || !empty($input)) {
    

    or:

    $input = $request->query->get('q');
    if (is_numeric($input) || !empty($input)) {
    

    Or use the method if we add that.

Based on the above, we should probably also add test coverage for other empty value types.

Thanks!

WalkingDexter’s picture

Status: Needs work » Needs review
FileSize
5.05 KB

Good points! I updated the patch with minor changes - empty() is not necessary when the variable is defined.

marcoliver’s picture

The updated patch in #14 works nicely!

As far as expanding the test coverage goes, would adding this to EntityAutocompleteTest.php suffice?

    $emptyValues = [
      [],
      NULL,
      FALSE,
      0,
      0.0,
    ];
    foreach($emptyValues as $emptyValue) {
      $data = $this->getAutocompleteResult($emptyValue);
      $this->assertEmpty($data);
    }
smustgrave’s picture

Status: Needs review » Needs work

@WalkingDexter fyi please include an interdiff to help see the changes

@marcoliver think that would be good!

WalkingDexter’s picture

FileSize
1.86 KB

@smustgrave Here it is.

xjm’s picture

Issue summary: View changes
marcoliver’s picture

@WalkingDexter Would you mind adding the lines I proposed in #15 (or something similar you like) to your patch / diff? Then we’d also have the expanded test coverage for all the other empty values @xjm suggested in #13.

I’d do it myself, but I don’t have access to a workable setup at this time.

xjm’s picture

Thanks @marcoliver. #15 is a decent suggestion. Usually I'd suggest using a data provider since that is cleaner, but since core/tests/Drupal/KernelTests/Core/Entity/EntityAutocompleteTest.php is already a kernel test set up to test many API values with a single test setup, it is more performant to just add code along the lines of what you suggest.

Saving issue credits.

allisonherodevs’s picture

FileSize
971 bytes

I added the suggested additional tests per comment #15 to the EntityAutocompleteTest.php file.

allisonherodevs’s picture

Added the tests to a new method instead of an existing method as I should've -- will update with new patch and interdiff shortly.

allisonherodevs’s picture

Updated the wrong method will post updated interdiff and patch shortly.

allisonherodevs’s picture

allisonherodevs’s picture

Status: Needs work » Needs review
smustgrave’s picture

Status: Needs review » Needs work

CC failure.

WalkingDexter’s picture

Improvements for tests.

I removed [] from the list of empty values because it causes an error:

1) Drupal\KernelTests\Core\Entity\EntityAutocompleteTest::testEntityReferenceAutocompletion
Symfony\Component\HttpFoundation\Exception\BadRequestException: Input value "q" contains a non-scalar value.

/var/www/web/vendor/symfony/http-foundation/InputBag.php:37
/var/www/web/core/modules/system/src/Controller/EntityAutocompleteController.php:82
/var/www/web/core/tests/Drupal/KernelTests/Core/Entity/EntityAutocompleteTest.php:209
/var/www/web/core/tests/Drupal/KernelTests/Core/Entity/EntityAutocompleteTest.php:142
/var/www/web/vendor/phpunit/phpunit/src/Framework/TestResult.php:728
ashley_herodev’s picture

Thanks at @WalkingDexter, I have added a test-only patch which should fail to prove the test coverage for #27

Status: Needs review » Needs work
xjm’s picture

Status: Needs work » Needs review
FileSize
5.7 KB

Thanks @allisonherodevs and @ashley_herodev! The test-only patch fails in the expected manner. 👍

I forgot to tell Ashley to re-upload the passing patch after the failing one so that it gets retested instead of that. So doing that here. Not my work, just a little housekeeping. :)

smustgrave’s picture

Status: Needs review » Reviewed & tested by the community

Confirmed additional test in #15 has been added.

lauriii’s picture

Status: Reviewed & tested by the community » Needs review
  1. +++ b/core/lib/Drupal/Core/Entity/Element/EntityAutocomplete.php
    @@ -203,7 +203,8 @@ public static function processEntityAutocomplete(array &$element, FormStateInter
    +    if (isset($element['#value']) && ($element['#value'] === '0' || $element['#value'])) {
    

    It seems that is_string with strlen or checking explicitly for empty string is how we would check emptiness of strings.

    I think !empty($element['#value']) || (is_string($element['#value']) && strlen($element['#value'])) might be more consistent with that.

  2. +++ b/core/modules/system/src/Controller/EntityAutocompleteController.php
    @@ -77,8 +77,12 @@ public static function create(ContainerInterface $container) {
    +    if ($input === '0' || $input) {
    

    Same here, I don't think we need to hardcode '0' here. We could do either is_string($input) && $input !== '' or is_string($input) && strlen($input).

smustgrave’s picture

Status: Needs review » Needs work
Issue tags: +Novice

Think this would be a good task for a novice user

roshni27’s picture

Status: Needs work » Needs review
FileSize
5.75 KB
1.5 KB

Based on the comments and suggestions #32, here's the updated code with more explicit checks for empty strings.

smustgrave’s picture

Status: Needs review » Reviewed & tested by the community

@roshni27 same post as the other issue thank you for working on it. Looking at your post history believe you can work on non-novice issue now. Novice issues are meant to be reserved for new users. Thanks!

roshni27’s picture

Thank you for your feedback! I appreciate your guidance on the issue categorization. I'll make sure to focus on non-novice issues going forward.

needs-review-queue-bot’s picture

Status: Reviewed & tested by the community » Needs work

The Needs Review Queue Bot tested this issue.

While you are making the above changes, we recommend that you convert this patch to a merge request. Merge requests are preferred over patches. Be sure to hide the old patch files as well. (Converting an issue to a merge request without other contributions to the issue will not receive credit.)

smustgrave’s picture

Hiding the other patches but I think this one was small enough that patches should still be fine.

xjm’s picture

Status: Reviewed & tested by the community » Needs work

Thanks @lauriii; that's cleaner.

+++ b/core/lib/Drupal/Core/Entity/Element/EntityAutocomplete.php
@@ -203,7 +203,8 @@ public static function processEntityAutocomplete(array &$element, FormStateInter
+    if (isset($element['#value']) && (!empty($element['#value']) || (is_string($element['#value']) && strlen($element['#value'])))) {

Checking isset() is redundant with !empty() because !empty() will always be false if isset() is false. So we can remove the isset() check and simplify the first condition.

pradhumanjain2311’s picture

Status: Needs work » Needs review
FileSize
5.72 KB

Made changes as per #39

smustgrave’s picture

Status: Needs review » Needs work

Please include any patches with an interdiff,

Also encouraged patches be converted to MRs.

ankithashetty made their first commit to this issue’s fork.

ankithashetty’s picture

Status: Needs work » Needs review
FileSize
1.09 KB

Raised an MR as requested.
Also attaching an interdiff of the patches in #34 and #40.

Thanks!

smustgrave’s picture

Status: Needs review » Reviewed & tested by the community

Feedback appears to be have been addressed

xjm’s picture

Also remember to queue tests when using patches. Merge requests will queue tests automatically and be much faster.

I queued the tests now, but we have to wait for them to finish running.

xjm’s picture

Ah, I see there is now an MR. Please remember to hide patch files from the IS when opening an MR. Thanks!

xjm’s picture

Chasing my own tail here, re-rebasing after I make other commits. Unfortunately the test-only job only works properly if the MR is rebased against the branch tip. This is a good reason for contributors to run the test-only job before RTBCing.

xjm’s picture

Status: Reviewed & tested by the community » Needs review

Finally have the test-only results:

Time: 00:03.659, Memory: 4.00 MB
There was 1 failure:
1) Drupal\KernelTests\Core\Entity\Element\EntityAutocompleteElementFormTest::testValidEntityAutocompleteElement
Failed asserting that null matches expected '3'.
/builds/project/drupal/vendor/phpunit/phpunit/src/Framework/Constraint/Equality/IsEqual.php:94
/builds/project/drupal/core/tests/Drupal/KernelTests/Core/Entity/Element/EntityAutocompleteElementFormTest.php:288
/builds/project/drupal/vendor/phpunit/phpunit/src/Framework/TestResult.php:728
FAILURES!
Tests: 4, Assertions: 56, Failures: 1.

I wonder why core/tests/Drupal/KernelTests/Core/Entity/EntityAutocompleteTest.php shows no failures? I would have expected these two assertions to fail:

    // Try to autocomplete an entity label with '0' character.
    $input = '0';
    $data = $this->getAutocompleteResult($input);
    $this->assertSame(Html::escape($entity_1->name->value), $data[0]['label'], 'Autocomplete returned the first matching entity');
    $this->assertSame(Html::escape($entity_2->name->value), $data[1]['label'], 'Autocomplete returned the second matching entity');

Can someone try it locally?

smustgrave’s picture

Status: Needs review » Reviewed & tested by the community

Ran EntityAutocompleteTest ::testEntityReferenceAutocompletion and got

Undefined array key 0

xjm’s picture

OK, so it seems like a bug in the test-only job then. Thanks @smustgrave!

smustgrave’s picture

Haven't review but saw this in the queue #3395977: Test-only changes reverts changes to test modules could be completely unrelated though

xjm’s picture

There are no changes to a test module or other such fixture in the MR though.

xjm’s picture

Saving credits.

  • xjm committed 68dfec3c on 11.x
    Issue #3383131 by WalkingDexter, xjm, allisonherodevs, ashley_herodev,...

  • xjm committed eab4bfa9 on 10.2.x
    Issue #3383131 by WalkingDexter, xjm, allisonherodevs, ashley_herodev,...

  • xjm committed 18732679 on 10.1.x
    Issue #3383131 by WalkingDexter, xjm, allisonherodevs, ashley_herodev,...
xjm’s picture

Version: 11.x-dev » 10.1.x-dev
Status: Reviewed & tested by the community » Fixed

I did one more full review of the whole merge request. It looks pretty great!

Committed to 11.x, 10.2.x, and 10.1.x as a patch-safe bugfix. (We were careful to eliminate disruptions by not changing the behavior of empty values other than string-zero.) Thanks everyone for your work on this!

  • xjm committed b78c8746 on 10.1.x
    Revert "Issue #3383131 by WalkingDexter, xjm, allisonherodevs,...

  • xjm committed 899166e3 on 10.2.x
    Revert "Issue #3383131 by WalkingDexter, xjm, allisonherodevs,...

  • xjm committed 7ddc1b8e on 11.x
    Revert "Issue #3383131 by WalkingDexter, xjm, allisonherodevs,...
xjm’s picture

Status: Fixed » Needs work

Sorry, I realized this morning why the test failure probably wasn't being surfaced correctly. (Let's call it a working illustration of why doing code reviews for 11 hours straight decreases your effectiveness.) The failure in #50 is a notice. We should make the fail more explicit so that an assertion fails -- the test is not working as intended currently.

Reverted to fix that. Thanks!

WalkingDexter’s picture

Status: Needs work » Needs review
smustgrave’s picture

Status: Needs review » Needs work

Seems to have a unit test failure now.

WalkingDexter’s picture

Status: Needs work » Needs review

Rebased the source branch, now all tests are passed. The test-only results now show the failure for core/tests/Drupal/KernelTests/Core/Entity/EntityAutocompleteTest.php.

smustgrave’s picture

Status: Needs review » Reviewed & tested by the community

LGTM

larowlan’s picture

Status: Reviewed & tested by the community » Needs work

Left a question on the MR, feel free to self-RTBC after answering

WalkingDexter’s picture

Status: Needs work » Reviewed & tested by the community

quietone made their first commit to this issue’s fork.

quietone’s picture

Version: 10.1.x-dev » 11.x-dev
Issue summary: View changes
Status: Reviewed & tested by the community » Needs work

I'm triaging RTBC issues.

I gather from #58 that this was reverted because the test-only tests did not fail for EntityAutocompleteTest ::testEntityReferenceAutocompletion. When I run the test-only version locally I get that same result as in #59,

1) Drupal\KernelTests\Core\Entity\EntityAutocompleteTest::testEntityReferenceAutocompletion
Undefined array key 0

/var/www/html/core/tests/Drupal/KernelTests/Core/Entity/EntityAutocompleteTest.php:139

That makes sense because it is testing an empty array. Changing the assertion to test against NULL doesn't make sense to me because that is not the real result from getAutocompleteResult. A simple assertion that the return value is an array would probably work. But before that I want to revert the last change and rebase.

quietone’s picture

Status: Needs work » Needs review

After running test-only changes I see the two expected errors, https://git.drupalcode.org/project/drupal/-/jobs/552187. So, that is working now.

Do we think there is a need to assert that getAutocompleteResult() returned an array with two elements?

smustgrave’s picture

Status: Needs review » Reviewed & tested by the community
    // Try to autocomplete an entity label with the '0' character.
    $input = '0';
    $data = $this->getAutocompleteResult($input);
    $this->assertSame(Html::escape($entity_1->name->value), $data[0]['label'], 'Autocomplete returned the first matching entity');
    $this->assertSame(Html::escape($entity_2->name->value), $data[1]['label'], 'Autocomplete returned the second matching entity');

Believe thats what is being tested here. Unless I'm misunderstanding.

  • larowlan committed 91c760c5 on 10.2.x
    Issue #3383131 by WalkingDexter, xjm, allisonherodevs, ashley_herodev,...

  • larowlan committed d7537753 on 11.x
    Issue #3383131 by WalkingDexter, xjm, allisonherodevs, ashley_herodev,...
larowlan’s picture

Version: 11.x-dev » 10.2.x-dev
Status: Reviewed & tested by the community » Postponed

Committed to 11.x and backported to 10.2.x

Thanks all

WalkingDexter’s picture

Should the status be "Fixed" instead of "Postponed"?

smustgrave’s picture

Status: Postponed » Fixed

Believe so as it appears to have been committed to both 11.c and 10.2.x

Status: Fixed » Closed (fixed)

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