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:
- Don't allow a FieldType to be created unless there is a widget available for it.
- Return a stub widget from
$this->defaultValueWidget
in FieldItemList. - 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
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
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. |
Comment | File | Size | Author |
---|---|---|---|
#59 | drupal-fieldtype_with_no-2552799-59.patch | 12.57 KB | jordanpagewhite |
#54 | interdiff_50-54.txt | 979 bytes | heddn |
#54 | drupal-fieldtype_with_no-2552799-54.patch | 12.56 KB | heddn |
Comments
Comment #2
heddnComment #3
heddnComment #4
heddnAnother 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?
Comment #6
heddnThis 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.
Comment #7
heddnImprove test coverage.
Comment #8
yched CreditAttribution: yched commentedYeah, 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.
That comment should really stay next to the line that sets #parents.
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.
Comment #9
yched CreditAttribution: yched commentedAlso, 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
Comment #10
heddnAdding tags for the feedback.
Comment #11
heddnComment #12
heddnComment #13
deepakaryan1988Addressed the points which is in #8
Comment #14
deepakaryan1988Comment #15
deepakaryan1988Anyone up for reviewing this patch?
Comment #16
glenshewchuck CreditAttribution: glenshewchuck as a volunteer commentedI'll review the patch (I'm a novice core contributor.)
Comment #17
heddnThere'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.
Comment #18
glenshewchuck CreditAttribution: glenshewchuck as a volunteer commentedBy the "Manage forms" screen, you mean the field edit form ?
Comment #19
glenshewchuck CreditAttribution: glenshewchuck as a volunteer commentedReproduced 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).
Comment #20
glenshewchuck CreditAttribution: glenshewchuck as a volunteer commentedThe Manage Form Display shows the field as hidden with no other options available.
Comment #21
heddnre #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?
Comment #22
glenshewchuck CreditAttribution: glenshewchuck as a volunteer commentedHere 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)
Comment #23
heddnI 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.
Comment #24
glenshewchuck CreditAttribution: glenshewchuck as a volunteer commentedI'll give that a try.
Comment #25
glenshewchuck CreditAttribution: glenshewchuck as a volunteer commentedThe ternary fix seems to work.
Comment #26
heddnCan we see about adding a php unit test for this? There are some examples in the patch already.
Comment #27
glenshewchuck CreditAttribution: glenshewchuck as a volunteer commentedwill look do tomorrow
Comment #28
glenshewchuck CreditAttribution: glenshewchuck as a volunteer commentedinterdiff
Comment #29
heddnPHP 5.3+ has an alternate method for ternary.
$configuration['type'] = isset($field_type['default_widget']) ?: NULL;
Comment #30
tim.plunkett@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...
Comment #31
heddnre #30: Thank you for the correction. I got myself confused with ?? coalescing operator in PHP 7.
Comment #32
larowlanlooking at tests
Comment #33
larowlanran out of time
Comment #34
luklambrechts CreditAttribution: luklambrechts commentedI will take a look at this one
Comment #35
yched CreditAttribution: yched commented@glenshewchuck #20 :
(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.
Comment #36
swentel CreditAttribution: swentel commentedThe same problem of the field not being hidden applies to the formatter too in case there's no default_formatter, see interdiff
Comment #37
heddnre #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.
Comment #38
yched CreditAttribution: yched commentedThanks @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 :-)
Comment #40
heddnThis 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.
Comment #43
heddnRandom test failure. Any suggestions for:
Comment #44
heddnTimestampItem 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.
Comment #45
XanoThis is (at least partly) a duplicate of #1864014: Field types specify non-existent default widgets and formatters.
Comment #46
XanoThen
@return
must specifynull
as well.Why do we do this?
Use PHP 5'5.s
::class
constant instead of strings with FQNs.Comment #47
XanoComment #48
heddnre #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.
Comment #49
heddnAddressing 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?
Comment #50
heddnInterdiff in #49 is fine. But the patch is bad. Let's actually upload something this time.
Comment #51
heddnAnd 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.
Comment #54
heddnFixed up #50. Interesting that #51 didn't fail. Maybe we aren't using that field type after all?
Comment #55
heddnOpened #2563843: MapItem is broken for configurable fields as a follow-up on MapItem to start the conversation.
Comment #56
jordanpagewhite CreditAttribution: jordanpagewhite as a volunteer commentedThe 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().
Comment #57
heddn@jordanpagewhite can you re-roll the patch?
Comment #58
heddnComment #59
jordanpagewhite CreditAttribution: jordanpagewhite as a volunteer commentedI re-rolled the patch from #54 to use getDefaultValueCallback() on line 299 of FieldItemList.php
Comment #60
jordanpagewhite CreditAttribution: jordanpagewhite as a volunteer commentedI 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.
Comment #61
yched CreditAttribution: yched commentedMinor, can be fixed on commit :
Nitpick , but that was mentioned in #8 :
This comment explains the line above (the one with #parents). Can we move it one line up ?
Comment #62
webchickComment moved, committed and pushed to 8.0.x. Thanks!