Problem/Motivation

Create a FieldType without any default widget and it results in a NULL $widget.

Error: Fatal error: Call to a member function form() on a non-object in core/lib/Drupal/Core/Field/FieldItemList.php on line 304

Steps to reproduce: Create a FieldType with minimal info. Example below. This draws from a cleaned-up: https://github.com/heddn/ejemplo_modulo/blob/034cb9531c9632fa953f14acb6a...

/**
 * Plugin implementation of the 'example' field type.
 *
 * @FieldType(
 *   id = "example",
 *   label = @Translation("Example"),
 *   description = @Translation("Example")
 * )
 */
class ExampleFieldType extends FieldItemBase {}

Proposed resolution

Options:

  1. Don't allow a FieldType to be created unless there is a widget available for it.
  2. Return a stub widget from $this->defaultValueWidget in FieldItemList.
  3. Check for NULL $widget in FieldItemList and don't try to access it if it is empty.

#3 is the decided approach.

While digging into this, it was also discovered that there was some odd happenings with "Manage form display" where a field type with a missing default_widget would initially be activated, then immediately de-activate. Causing a DX/UX issue. This is now also addressed in the latest fixes.

Remaining tasks

Contributor tasks needed
Task Novice task? Contributor instructions Complete?
Add automated tests Instructions
Update the patch to incorporate feedback from reviews (include an interdiff) Novice Instructions Done
Manually test to check what happens on the "Manage forms" screen if a field has no available widget. Novice Instructions done

User interface changes

API changes

Data model changes

Beta phase evaluation

Reference: https://www.drupal.org/core/beta-changes
Issue category Bug because a Fatal error is thrown.
Issue priority Major because it happens under rare circumstances or affecting only a small percentage of all users.
CommentFileSizeAuthor
#59 drupal-fieldtype_with_no-2552799-59.patch12.57 KBjordanpagewhite
#54 interdiff_50-54.txt979 bytesheddn
#54 drupal-fieldtype_with_no-2552799-54.patch12.56 KBheddn
#51 drupal-fieldtype_with_no-2552799-51.patch15.88 KBheddn
#50 drupal-fieldtype_with_no-2552799-50.patch13.07 KBheddn
#49 interdiff_47-49.txt2.59 KBheddn
#49 drupal-fieldtype_with_no-2552799-49.patch0 bytesheddn
#47 drupal_2552799_47.patch11.45 KBXano
#47 interdiff.txt4.6 KBXano
#44 drupal-fieldtype_with_no-2552799-44.patch11.33 KBheddn
#44 interdiff_40-44.txt2.07 KBheddn
#40 interdiff_36-40.txt5.1 KBheddn
#40 drupal-fieldtype_with_no-2552799-40.patch8.65 KBheddn
#3 drupal-call_to_a_member-2552799-3-tests.patch1.19 KBheddn
#3 drupal-call_to_a_member-2552799-3.patch2.32 KBheddn
#6 drupal-call_to_a_member-2552799-5.patch6.47 KBheddn
#6 interdiff_3-5.txt5.68 KBheddn
#7 drupal-call_to_a_member-2552799-7.patch6.46 KBheddn
#7 interdiff_5-7.txt969 bytesheddn
#13 drupal-call_to_a_member-2552799-13.patch6.46 KBdeepakaryan1988
#13 interdiff-2552799-7-13.txt1.08 KBdeepakaryan1988
#19 Manage-Form-Screen-Shot.png39.03 KBglenshewchuck
#20 Manage_Form_Display.png41.63 KBglenshewchuck
#25 drupal-call_to_a_member-2552799-25.patch7.31 KBglenshewchuck
#28 interdiff-2552799-13-25.txt748 bytesglenshewchuck
#36 interdiff.txt1.98 KBswentel
#36 drupal-call_to_a_member-2552799-36.patch9.29 KBswentel
Support from Acquia helps fund testing for Drupal Acquia logo

Comments

heddn created an issue. See original summary.

heddn’s picture

Issue summary: View changes
heddn’s picture

heddn’s picture

Title: Call to a member function form() on a non-object in core/lib/Drupal/Core/Field/FieldItemList.php on line 304 » FieldType with no available widget causes Fatal error
Issue summary: View changes

Another idea I just had, instead of providing an empty elements array, and thereby an empty field edit form, could we provide some helper text that says this field type has no available widget? Otherwise it is confusing to users. And might help with the UX.

Should it be a '#markup' render array or an actual error message?

The last submitted patch, 3: drupal-call_to_a_member-2552799-3-tests.patch, failed testing.

heddn’s picture

This adds a '#markup' message if the field type doesn't have a supported widget available. I also fix some outstanding bugs that were masked by not returning a widget.

heddn’s picture

yched’s picture

Status: Needs review » Needs work

Yeah, all field types are supposed to have an available widget, if they don't we can't show an input for the default value in the UI.
But we can't break with a fatal if that happens, so I guess the proposed approach seems reasonable.

We'd probably need to check what happens on the "Manage forms" screen if a field has no available widget.

  1. +++ b/core/lib/Drupal/Core/Field/FieldItemList.php
    @@ -296,15 +296,17 @@ public function getConstraints() {
    +    // Place the input in a separate place in the submitted values tree.
    

    That comment should really stay next to the line that sets #parents.

  2. +++ b/core/lib/Drupal/Core/Field/FieldItemList.php
    @@ -296,15 +296,17 @@ public function getConstraints() {
    +    return ['#markup' => $this->t('No widget available for: %type.', ['%type' => $this->getFieldDefinition()->getDataType()])];
    

    This should go in an "else" for the "if ($widget = ...)" case, we don't want that for the case there is a default_value_callback.

yched’s picture

Also, note that this will slightly conflict with #2529034: Replace direct access to FieldConfigBase::default_value with methods. Depending on which gets in first, the other one will need a reroll

heddn’s picture

Issue tags: +Novice

Adding tags for the feedback.

heddn’s picture

Issue summary: View changes
heddn’s picture

Issue summary: View changes
deepakaryan1988’s picture

Addressed the points which is in #8

deepakaryan1988’s picture

Status: Needs work » Needs review
deepakaryan1988’s picture

Anyone up for reviewing this patch?

glenshewchuck’s picture

I'll review the patch (I'm a novice core contributor.)

heddn’s picture

Issue summary: View changes

There's also an outstanding task in the remaining tasks to manually see what is happening with the "Manage forms" screen if a field has no available widget.

glenshewchuck’s picture

By the "Manage forms" screen, you mean the field edit form ?

glenshewchuck’s picture

FileSize
39.03 KB

Reproduced error. Applied patch and tested. The "Manage forms" screen (edit field) looks good (see attached) but received an error in the dblog when on the field edit form, I'm investigating.

Notice: Undefined index: default_widget in Drupal\Core\Field\WidgetPluginManager->prepareConfiguration() (line 154 of /Users/glenshewchuck/Sites/drupal8/htdocs/core/lib/Drupal/Core/Field/WidgetPluginManager.php).

glenshewchuck’s picture

FileSize
41.63 KB

The Manage Form Display shows the field as hidden with no other options available.

heddn’s picture

Status: Needs review » Needs work

re #19: that error is probably related to a missing default widget. Can you dig into why and see if we can rectify things in this patch?

glenshewchuck’s picture

Here is the error and backtrace from when I go into the edit field form.

Notice: Undefined index: default_widget in Drupal\Core\Field\WidgetPluginManager->prepareConfiguration() (line 154 of core/lib/Drupal/Core/Field/WidgetPluginManager.php).
Drupal\Core\Field\WidgetPluginManager->prepareConfiguration('example_field_type', Array)
Drupal\Core\Field\WidgetPluginManager->getInstance(Array)
Drupal\Core\Field\FieldItemList->defaultValueWidget(Object)
Drupal\Core\Field\FieldItemList->defaultValuesForm(Array, Object)
Drupal\field_ui\Form\FieldConfigEditForm->form(Array, Object)
Drupal\Core\Entity\EntityForm->buildForm(Array, Object)
call_user_func_array(Array, Array)
Drupal\Core\Form\FormBuilder->retrieveForm('field_config_edit_form', Object)
Drupal\Core\Form\FormBuilder->buildForm(Object, Object)
Drupal\Core\Controller\FormController->getContentResult(Object, Object)
call_user_func_array(Array, Array)
Drupal\Core\EventSubscriber\EarlyRenderingControllerWrapperSubscriber->Drupal\Core\EventSubscriber\{closure}()
Drupal\Core\Render\Renderer->executeInRenderContext(Object, Object)
Drupal\Core\EventSubscriber\EarlyRenderingControllerWrapperSubscriber->wrapControllerExecutionInRenderContext(Array, Array)
Drupal\Core\EventSubscriber\EarlyRenderingControllerWrapperSubscriber->Drupal\Core\EventSubscriber\{closure}()
call_user_func_array(Object, Array)
Symfony\Component\HttpKernel\HttpKernel->handleRaw(Object, 1)
Symfony\Component\HttpKernel\HttpKernel->handle(Object, 1, 1)
Drupal\Core\StackMiddleware\Session->handle(Object, 1, 1)
Drupal\Core\StackMiddleware\KernelPreHandle->handle(Object, 1, 1)
Drupal\page_cache\StackMiddleware\PageCache->pass(Object, 1, 1)
Drupal\page_cache\StackMiddleware\PageCache->handle(Object, 1, 1)
Drupal\Core\StackMiddleware\ReverseProxyMiddleware->handle(Object, 1, 1)
Drupal\Core\StackMiddleware\NegotiationMiddleware->handle(Object, 1, 1)
Stack\StackedHttpKernel->handle(Object, 1, 1)
Drupal\Core\DrupalKernel->handle(Object)

heddn’s picture

I think you can just use a ternary in WidgetPluginManager, line 154 and set the value to NULL. Try that. We should also add a php unit test against WidgetPluginManager->prepareConfiguration() to test this scenario.

glenshewchuck’s picture

I'll give that a try.

glenshewchuck’s picture

The ternary fix seems to work.

heddn’s picture

Issue tags: +Needs tests

Can we see about adding a php unit test for this? There are some examples in the patch already.

glenshewchuck’s picture

will look do tomorrow

glenshewchuck’s picture

FileSize
748 bytes

interdiff

heddn’s picture

+++ b/core/lib/Drupal/Core/Field/WidgetPluginManager.php
@@ -151,7 +151,7 @@ public function prepareConfiguration($field_type, array $configuration) {
+      $configuration['type'] = (isset($field_type['default_widget']) ? $field_type['default_widget'] : NULL);

PHP 5.3+ has an alternate method for ternary.

$configuration['type'] = isset($field_type['default_widget']) ?: NULL;

tim.plunkett’s picture

@heddn, nope, that will return either TRUE or NULL, but never $field_type['default_widget']. ?: will cause it to return isset($field_type['default_widget']) when TRUE...

heddn’s picture

re #30: Thank you for the correction. I got myself confused with ?? coalescing operator in PHP 7.

larowlan’s picture

Assigned: Unassigned » larowlan

looking at tests

larowlan’s picture

Assigned: larowlan » Unassigned

ran out of time

luklambrechts’s picture

I will take a look at this one

yched’s picture

@glenshewchuck #20 :

The Manage Form Display shows the field as hidden with no other options available

(https://www.drupal.org/files/issues/Manage_Form_Display.png)

Thanks for checking that !
There is indeed a slight issue that, in this case, the field should appear in the "Disabled" section at the bottom of the screen (like when you select "- Hidden -" for one of the other fields, it gets moved to the "Disabled" section.

swentel’s picture

Status: Needs work » Needs review
FileSize
9.29 KB
1.98 KB

The same problem of the field not being hidden applies to the formatter too in case there's no default_formatter, see interdiff

heddn’s picture

Status: Needs review » Needs work

re #36: I had noticed that too, but it automatically got moved to disabled when trying to use it. Causing a slight pause. But then I moved on. This should help with that.

Still missing tests, so bumping back to needs work.

yched’s picture

Thanks @swentel, makes sense. Not sure we need the "else" part though, there should be nothing to remove since we're creating the field anyway. It's rather "only do setComponent() if there is actually a $widget_id" ?

(also, minor, the $options var could be inlined :-)

The last submitted patch, 36: drupal-call_to_a_member-2552799-36.patch, failed testing.

heddn’s picture

This will probably fail some more tests. I tracked down where the failures were coming from. In FieldStorageAddForm, unless the field has pre-configured field storage options, it doesn't collect a plugin id. So lots of fields were disabled/hidden.

I've figured out how to do what we want in EntityDisplayFormBase, but that has the interesting side-effect of finding another issue. BaseFieldDefinition::create() doesn't set a default plugin. So Node 'Authored on' field is always getting hidden.

This picks up a few small things I noticed when looking at the patch, in addition to attempting to fix the failed tests.

Status: Needs review » Needs work

The last submitted patch, 40: drupal-fieldtype_with_no-2552799-40.patch, failed testing.

heddn’s picture

Random test failure. Any suggestions for:

BaseFieldDefinition::create() doesn't set a default plugin. So Node 'Authored on' field is always getting hidden.

heddn’s picture

Issue summary: View changes
Status: Needs work » Needs review
Issue tags: -Novice
FileSize
2.07 KB
11.33 KB

TimestampItem didn't have a default_widget; that was causing the issues with 'Authored on'. And actually, none of the date/time field types have default_widgets. Fixed now.

EntityReferenceItem doesn't either. But that class should either be abstract or something. Since entity_reference_field_info_alter overwrites the entity_reference info so it is not accessible nor used directly. For now, I've fixed EntityReferenceItem by using the same default widget that is used by ConfigurableEntityReferenceItem, namely "entity_reference_autocomplete". I fixed it because someone else might model from it and it is broken code just waiting to be uncovered.

MapItem doesn't have a default_widget either. But it is a confusing field because I don't ever see it used... What is it?

FieldTestItem is a test class that has no default_widget. Same with ShapeItem.

Still missing the necessary tests to uncover the bugs with a fieldtype missing a default_widget. Updated issue summary.

Xano’s picture

Xano’s picture

  1. +++ b/core/lib/Drupal/Core/Field/FieldItemList.php
    @@ -349,7 +355,7 @@ public static function processDefaultValue($default_value, FieldableEntityInterf
        * @return \Drupal\Core\Field\WidgetInterface
    -   *   A Widget object.
    +   *   A Widget object or NULL if no widget is available.
    

    Then @return must specify null as well.

  2. +++ b/core/modules/field_ui/src/Form/EntityDisplayFormBase.php
    @@ -279,6 +279,13 @@ protected function buildFieldRow(FieldDefinitionInterface $field_definition, arr
    +    }
    

    Why do we do this?

  3. +++ b/core/tests/Drupal/Tests/Core/Field/FieldItemListTest.php
    @@ -155,4 +156,68 @@ public function testEqualsEmptyItems() {
    +    $field_definition = $this->getMock('DrupalFormStateInterface\Core\Field\FieldDefinitionInterface', ['getType']);
    

    Use PHP 5'5.s ::class constant instead of strings with FQNs.

Xano’s picture

heddn’s picture

re #46.2: See #35 / #36. Basically, the plugin gets disabled for you on first save of the EntityForm or EntityView if it has absolutely no widget available. Which is really confusing to the user. Why is it disabled? Because... do google search... Waste a bunch of time...

But that brings up a good point. If the real issue isn't that there is or isn't a default plugin and really that there are NO plugins, we should maybe just call $this->getPluginOptions($field_definition), array filter for 'hidden' and see if we have an empty array. At which point, then de-activate the plugin.

heddn’s picture

Addressing feedback in #47. I manually tested, and this no longer disables a field that has an applicable widget (but no default widget). Do we still need some type of additional testing here?

heddn’s picture

Interdiff in #49 is fine. But the patch is bad. Let's actually upload something this time.

heddn’s picture

And just to see what happens, let's try removing MapItem. I'd like to see if/where it is used. I'm not suggesting we actually remove it.

The last submitted patch, 50: drupal-fieldtype_with_no-2552799-50.patch, failed testing.

Status: Needs review » Needs work

The last submitted patch, 51: drupal-fieldtype_with_no-2552799-51.patch, failed testing.

heddn’s picture

Fixed up #50. Interesting that #51 didn't fail. Maybe we aren't using that field type after all?

heddn’s picture

Opened #2563843: MapItem is broken for configurable fields as a follow-up on MapItem to start the conversation.

jordanpagewhite’s picture

The patch failed to apply. It output, "error: while searching for:" and then outputs the defaultValueForm public function within FieldItemList.php. I will explain what it looks like is happening to me below. If I've overlooked something while applying the patch, let me know. Thanks.

It looks like the patch is searching for $this->getFieldDefinition()->default_value_callback within the public function defaultValuesForm within /core/lib/Drupal/Core/Field/FieldItemList.php starting on line 297. In my version of core, I have $this->getFieldDefinition()->getDefaultValueCallback().

heddn’s picture

Issue tags: -Needs tests +Needs reroll

@jordanpagewhite can you re-roll the patch?

heddn’s picture

Status: Needs review » Needs work
jordanpagewhite’s picture

I re-rolled the patch from #54 to use getDefaultValueCallback() on line 299 of FieldItemList.php

jordanpagewhite’s picture

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

I have rerolled this patch #54 to work with the latest 8.0.x branch. There was a minor edit needed to match a changed method call (see patch for details). The patch applies cleaning and appropriately checks for NULL $widget in FieldItemList and won't try to access it if it is empty, as described in the proposed resolution in the issue summary. The patch also, in the case of NULL $widget, displays 'No widget available....' with #markup.

yched’s picture

Minor, can be fixed on commit :

+++ b/core/lib/Drupal/Core/Field/FieldItemList.php
@@ -297,13 +297,17 @@ public function getConstraints() {
+        // Place the input in a separate place in the submitted values tree.
+        $element += $widget->form($this, $element, $form_state);

Nitpick , but that was mentioned in #8 :
This comment explains the line above (the one with #parents). Can we move it one line up ?

webchick’s picture

Status: Reviewed & tested by the community » Fixed

Comment moved, committed and pushed to 8.0.x. Thanks!

  • webchick committed f771655 on 8.0.x
    Issue #2552799 by heddn, glenshewchuck, Xano, deepakaryan1988, swentel,...

Status: Fixed » Closed (fixed)

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