Problem/Motivation

When submitting an entityreference field setting that is marked as required, a fatal error occurs (see below).

Steps to reproduce

  1. Fresh D8 installation (minimal profile)
  2. Enabled Field UI
  3. Added new node type "story"
  4. Added a new EntityReference Field "field_story_ref" (Content Types: Story, Required: unchecked) => No Error
  5. Editing field_story_ref without changing any field setting => No Error
  6. Editing field_story_ref with Required checked => No Error
  7. Editing field_story_ref with Required still checked => Error occurs (see below)
  8. Editing field_story_ref with Required unchecked => Error occurs too (see below)

Field settingError message

Error Message 1

Notice: Undefined index: target_id in Drupal\entity_reference\Plugin\Field\FieldWidget\AutocompleteWidgetBase->errorElement() (line 108 of /WEBSITES/undpaul/undsyncer/pages/undsyncer/drupal/core/modules/entity_reference/src/Plugin/Field/FieldWidget/AutocompleteWidgetBase.php).
Drupal\entity_reference\Plugin\Field\FieldWidget\AutocompleteWidgetBase->errorElement(Array, Object, Array, Object)
Drupal\Core\Field\WidgetBase->flagErrors(Object, Object, Array, Object)
Drupal\Core\Field\FieldItemList->defaultValuesFormValidate(Array, Array, Object)
Drupal\field_ui\Form\FieldEditForm->validateForm(Array, Object)
call_user_func_array(Array, Array)
Drupal\Core\Form\FormValidator->executeValidateHandlers(Array, Object)
Drupal\Core\Form\FormValidator->doValidateForm(Array, Object, 'field_ui_field_edit_form')
Drupal\Core\Form\FormValidator->validateForm('field_ui_field_edit_form', Array, Object)
Drupal\Core\Form\FormBuilder->processForm('field_ui_field_edit_form', Array, Object)
Drupal\Core\Form\FormBuilder->buildForm(Object, Object)
Drupal\Core\Controller\FormController->getContentResult(Object)
call_user_func_array(Array, Array)
Drupal\Core\Controller\HtmlPageController->getContentResult(Object, Array)
Drupal\Core\Controller\HtmlPageController->content(Object, Array)
call_user_func_array(Array, Array)
Symfony\Component\HttpKernel\HttpKernel->handleRaw(Object, 1)
Symfony\Component\HttpKernel\HttpKernel->handle(Object, 1, 1)
Drupal\Core\StackMiddleware\KernelPreHandle->handle(Object, 1, 1)
Drupal\Core\StackMiddleware\PageCache->handle(Object, 1, 1)
Drupal\Core\StackMiddleware\ReverseProxyMiddleware->handle(Object, 1, 1)
Stack\StackedHttpKernel->handle(Object, 1, 1)
Drupal\Core\DrupalKernel->handle(Object)
Recoverable fatal error: Argument 1 passed to Drupal\Core\Form\FormState::setError() must be of the type array, null given, called in /WEBSITES/undpaul/undsyncer/pages/undsyncer/drupal/core/lib/Drupal/Core/Field/WidgetBase.php on line 431 and defined in Drupal\Core\Form\FormState->setError() (line 1056 of /WEBSITES/undpaul/undsyncer/pages/undsyncer/drupal/core/lib/Drupal/Core/Form/FormState.php).
Drupal\Core\Form\FormState->setError(NULL, 'This value should not be null.')
Drupal\Core\Field\WidgetBase->flagErrors(Object, Object, Array, Object)
Drupal\Core\Field\FieldItemList->defaultValuesFormValidate(Array, Array, Object)
Drupal\field_ui\Form\FieldEditForm->validateForm(Array, Object)
call_user_func_array(Array, Array)
Drupal\Core\Form\FormValidator->executeValidateHandlers(Array, Object)
Drupal\Core\Form\FormValidator->doValidateForm(Array, Object, 'field_ui_field_edit_form')
Drupal\Core\Form\FormValidator->validateForm('field_ui_field_edit_form', Array, Object)
Drupal\Core\Form\FormBuilder->processForm('field_ui_field_edit_form', Array, Object)
Drupal\Core\Form\FormBuilder->buildForm(Object, Object)
Drupal\Core\Controller\FormController->getContentResult(Object)
call_user_func_array(Array, Array)
Drupal\Core\Controller\HtmlPageController->getContentResult(Object, Array)
Drupal\Core\Controller\HtmlPageController->content(Object, Array)
call_user_func_array(Array, Array)
Symfony\Component\HttpKernel\HttpKernel->handleRaw(Object, 1)
Symfony\Component\HttpKernel\HttpKernel->handle(Object, 1, 1)
Drupal\Core\StackMiddleware\KernelPreHandle->handle(Object, 1, 1)
Drupal\Core\StackMiddleware\PageCache->handle(Object, 1, 1)
Drupal\Core\StackMiddleware\ReverseProxyMiddleware->handle(Object, 1, 1)
Stack\StackedHttpKernel->handle(Object, 1, 1)
Drupal\Core\DrupalKernel->handle(Object)

Follow Up

After doing a quick workaround (I guess this might not be the real solution) to avoid the fatal error in \Drupal\entity_reference\Plugin\Field\FieldWidget\AutocompleteWidgetBase::errorElement():

public function errorElement(array $element, ConstraintViolationInterface $error, array $form, FormStateInterface $form_state) {
    return $element;
}

the validation error "This value should not be null." occures.

Follow up validation error

I'm currently not sure whether we should handle that as a separate issue or it tightly belongs to the fatal error.

Proposed resolution

Select the correct element in \Drupal\entity_reference\Plugin\Field\FieldWidget\AutocompleteWidgetBase::errorElement() for assigning the error.

Remaining tasks

  • Fixing \Drupal\entity_reference\Plugin\Field\FieldWidget\AutocompleteWidgetBase::errorElement()
  • Fixing the required field setting default value problem for the Autocomplete widget in this or a separate issue
Contributor tasks needed
Task Novice task? Contributor instructions Complete?
Manually test the patch Novice Instructions
Investigate to confirm whether the problem applies to all fields Novice
Add automated tests (See #10) Instructions

User interface changes

API changes

Comments

tstoeckler’s picture

Status: Active » Needs review
StatusFileSize
new1.11 KB

Here's a test proving that this is broken.

Upgrading to critical, because this is a Fatal you can trigger from the UI and adding a required Entity reference field is not really an edge case.

Status: Needs review » Needs work

The last submitted patch, 1: 2354685-fail.patch, failed testing.

tstoeckler’s picture

Priority: Major » Critical
tstoeckler’s picture

Status: Needs work » Needs review
StatusFileSize
new848 bytes
new1.93 KB

This implements the solution from the issue summary. Since WidgetBase already has an errorElement() method which just returns $element I removed the entire method.

We might want to commit this as is, it's certainly an improvement over the status quo and the fix is correct.

The problem mentioned in the issue summary remains, however.

amateescu’s picture

StatusFileSize
new1.83 KB

Nope, there's nothing wrong with \Drupal\entity_reference\Plugin\Field\FieldWidget\AutocompleteWidgetBase::errorElement(), this bug is a bit more subtle than that.

The problem here is that \Drupal\Core\Field\FieldItemList::defaultValuesFormValidate() calls the validate() method on itself but it does not account for the fact that, when dealing with the default value form / widget, the field definition should *not* be required. See the code that does this for the widget form itself in \Drupal\Core\Field\FieldItemList::defaultValueWidget().

Here's the correct fix :)

amateescu’s picture

StatusFileSize
new1.82 KB
new996 bytes

Also fixed a comment.

spadxiii’s picture

Status: Needs review » Reviewed & tested by the community

I just ran into this issue myself as well. It also fixes the problem that you can't switch the reference method for a required field. (The ajax-request generates the error about the target_id as well)

After applying this patch (to d8 beta2), these problems were gone!

tstoeckler’s picture

Hmm... #4 fixes a separate bug so is legit as well IMO.

amateescu’s picture

@tstoeckler, I don't see what bug is being fixed by #4.

Field widgets that do not return the $element they receive in formElement() and wrap it in another array, have to implement errorElement() to let the system know about their wrapper. See PathWidget and NumberWidget for other examples and the docblock of WidgetInterface::errorElement().

alexpott’s picture

Status: Reviewed & tested by the community » Needs work
+++ b/core/lib/Drupal/Core/Field/FieldItemList.php
@@ -336,6 +336,9 @@ public function defaultValuesFormValidate(array $element, array &$form, FormStat
+    // Force a non-required field definition.
+    // @see self::defaultValueWidget().
+    $this->definition->required = FALSE;

--- a/core/modules/entity_reference/src/Tests/EntityReferenceAdminTest.php
+++ b/core/modules/entity_reference/src/Tests/EntityReferenceAdminTest.php

This affects all fields - right? Seems strange to bury the test for this in EntityReferenceAdminTest - I would have expected a test in ManageFieldsTest.

tstoeckler’s picture

Re #9: The thing is that AutocompleteWidgetBase does not define a 'target_id' sub-element. That's why the current implementation of errorElement() is wrong. It is proven by the "undefined index" error in the IS and in #1.

amateescu’s picture

Ohboy...


  public function formElement(FieldItemListInterface $items, $delta, array $element, array &$form, FormStateInterface $form_state) {
    $entity = $items->getEntity();

    // Prepare the autocomplete route parameters.
    $autocomplete_route_parameters = array(
      'type' => $this->getSetting('autocomplete_type'),
      'field_name' => $this->fieldDefinition->getName(),
      'entity_type' => $entity->getEntityTypeId(),
      'bundle_name' => $entity->bundle(),
    );

    if ($entity_id = $entity->id()) {
      $autocomplete_route_parameters['entity_id'] = $entity_id;
    }

    $element += array(
      '#type' => 'textfield',
      '#maxlength' => 1024,
      '#default_value' => implode(', ', $this->getLabels($items, $delta)),
      '#autocomplete_route_name' => 'entity_reference.autocomplete',
      '#autocomplete_route_parameters' => $autocomplete_route_parameters,
      '#size' => $this->getSetting('size'),
      '#placeholder' => $this->getSetting('placeholder'),
      '#element_validate' => array(array($this, 'elementValidate')),
      '#autocreate_uid' => ($entity instanceof EntityOwnerInterface) ? $entity->getOwnerId() : \Drupal::currentUser()->id(),
    );

    return array('target_id' => $element);
  }

amateescu’s picture

@tstoeckler, are we looking at the same code here? :)

http://cgit.drupalcode.org/drupal/tree/core/modules/entity_reference/src...

tstoeckler’s picture

Hmm... you're right. Sorry about that. Still patch #1 proves that this is "valid" with some definition of valid. I'll have to debug that in detail. But in that case let's proceed here. Sorry for holding this up.

yesct’s picture

Issue summary: View changes
Issue tags: +Needs issue summary update

adding needs issue summary update, because I'm not sure if the items in the remaining tasks still need to be done, or if there needs to be a separate issue opened.
If this is going to be used for the Friday Critical Office Hours, then we need to be more clear here.

amateescu’s picture

Heh, I was wondering earlier today what happened to this issue.

IMO, both the proposed resolution and the remaining tasks from the current issue summary are inaccurate because there's nothing wrong with \Drupal\entity_reference\Plugin\Field\FieldWidget\AutocompleteWidgetBase::errorElement(), as proven by the patch in #6.

The only thing needed here in addition to #6 is a test in ManageFieldsTest that checks the ->required property of the field definition to be always FALSE, regardless of the actual (stored) field definition.

swentel’s picture

I've quickly checked to start a test in managefields in testDefaultValue(). However, it wasn't possible to get this fail on test_field.
So I'm starting to doubt this is indeed the right fix and starting to slowly agree with @tstoeckler that the problem lies in AutocompleteWidgetBase()/AutocompleteWidget.

amateescu’s picture

Status: Needs work » Needs review
StatusFileSize
new3.32 KB
new1.5 KB

Here you go :) Apparently it only happens when the field has unlimited cardinality.

swentel’s picture

Status: Needs review » Reviewed & tested by the community
Issue tags: +Ghent DA sprint

Oh wow, never thought about that .. ok looks fine to me.

Also tagging.

swentel’s picture

Status: Reviewed & tested by the community » Needs review
+++ b/core/modules/field_ui/src/Tests/ManageFieldsTest.php
@@ -359,6 +359,24 @@ function testDefaultValue() {
+    $this->assertText("Saved $field_name configuration", 'The form was successfully submitted.');

Actually, shouldn't we use format_string here ?

swentel’s picture

Status: Needs review » Reviewed & tested by the community

So we don't do it either in the same test. Could potentially be a novice follow up to change those, but shouldn't hold up this patch.

Status: Reviewed & tested by the community » Needs work

The last submitted patch, 18: 2354685-field-test-only.patch, failed testing.

swentel’s picture

Status: Needs work » Reviewed & tested by the community

Good to go

alexpott’s picture

Status: Reviewed & tested by the community » Fixed
Issue tags: -Needs issue summary update

This issue addresses a critical bug and is allowed per https://www.drupal.org/core/beta-changes. Committed 01d9799 and pushed to 8.0.x. Thanks!

  • alexpott committed 01d9799 on 8.0.x
    Issue #2354685 by amateescu, tstoeckler, derhasi: Fatal Error on re-...

Status: Fixed » Closed (fixed)

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