Closed (fixed)
Project:
Drupal core
Version:
8.5.x-dev
Component:
field system
Priority:
Major
Category:
Bug report
Assigned:
Unassigned
Issue tags:
Reporter:
Created:
28 Jan 2018 at 22:42 UTC
Updated:
12 Mar 2018 at 08:51 UTC
Jump to comment: Most recent, Most recent file
Comments
Comment #2
xjmComment #3
xjmComment #4
xjmComment #5
xjmComment #6
xjmAh, as mentioned.
Comment #7
amateescu commentedI can't think of a way to make proposed resolution #1 work without breaking any existing code, so here's a review for the current patch:
The empty line here needs to be moved after the last
}:)We can not pass a value for the delta because multiple value widgets handle "all deltas" with a single form element.
This references needs to point to
formMultipleElements()instead.Since this hook is altering a specific widget (entity_reference_autocomplete), we don't need to check the #type of the form element.
How about setting a
#cardinalityproperty in\Drupal\Core\Field\WidgetBase::formSingleElement(), right at the top where we append various stuff to$element?The
entity_reference_autocompletewidget does not handle multiple values, onlyentity_reference_autocomplete_tagsdoes, so I assume this hook implementation is broken?And as a general observation, the two hook implementations are duplicating quite a bit of non-trivial code (like access checking), is there a way to re-use some of that code?
Comment #8
xjmThanks @amateescu!
Comment #9
xjmWhoops the media stuff was not supposed to be in the patch at all. xjm git failure. I'll move the relevant bits of review etc. to the other issue. Thanks!
Comment #10
xjmWhoops, right, it was just whatever the last
$deltawas from the foreach over the individual values. Which is not right at all.Attached addresses #7 (minus the things about the media implementation which weren't meant to be included here). I've made the cardinality available both as context for the alter hook and as part of the render element as suggested in #7.
Sounds like @amateescu is okay with the approach here, so untagging subsystem maintainer review (though of course further review is welcome). Thanks!
Still needs tests and a CR.
Comment #11
jonathanshawYou forgot your attachment @xjm :)
Comment #12
xjm🙄 derp
Comment #13
tedbowIt might need different name but what would be the downside calling the hook from this point always not just if
$is_multiple == TRUE.I thinking if I had some alter logic I wanted to apply to say all entity reference widgets and that logic covered both single value and multi-value fields I would have to implement this hook and
hook_field_widget_WIDGET_TYPE_form_alter.Where as if this was always fired then I could just implement this new hook.
Comment #15
xjmIf @tedbow's suggestion works out we'd want to update this paragraph appropriately, and possibly even deprecate the existing hooks' behavior as well.
These should use more specific data types than
array; I'll fix this on the next reroll while I look into @tedbow's suggestion.Meanwhile, fixing those pesky syntax errors.
Comment #16
xjmDrafted a CR; we can update it with example code once we finalize the implementation here: https://www.drupal.org/node/2940780
Comment #17
xjmI tested @tedbow's suggestion out on #2936293: At least inform content authors where they can list and add media and it works quite well, so I'll update that here.
Comment #18
xjmAttached implements the new approach. This should allow much cleaner implementations that avoid the sort of code duplication @amateescu pointed out from the other issue.
I think we might want to completely deprecate the existing hooks since these will meet both usecases (https://www.drupal.org/core/deprecation#how-hook). I haven't done that yet here though since that probably needs a +1 from @amateescu.
Next: tests.
Comment #19
xjmProbably actually should also just take the previous example code now and put it in the new examples with the
Element::children()bit.Comment #20
xjmThere is an existing test implementation of
hook_field_widget_form_alter(), but as far as I can tell a lot of it is dead code. There's two places that do seem to use the implementation:Drupal\field\Tests\FormTest::testFieldFormSingle()and, oddly,Drupal\field_ui\Tests\ManageFieldsTest::helpDescriptions(). It looks like the remaining assertions started out in two of the much larger field tests in #1356824: No way for hook_field_widget_form_alter() to know if it is on a form or in defaults (prior to the Great Test Refactoring of 2012).Just verifying that the other seemingly dead code in the implementation is actually dead code with the attached.
I also addressed #19.
Comment #21
benjifisherIt looks as though #1356824: No way for hook_field_widget_form_alter() to know if it is on a form or in defaults (currently NR for the D7 patch) added code to test
hook_field_widget_form_alter()infield/lib/Drupal/field/Tests/FormTest.phpandfield_ui/lib/Drupal/field_ui/Tests/AlterTest.php, and then #1875992: Add EntityFormDisplay objects for entity forms removedAlterTest.php, and then #2084987: Remove usage of field_ui_default_value and recommend proper replacement added the code toManageFieldsTest.php.As far as I can tell from checking the git log, the second bit of test code was missing in between those two commits.
Comment #22
benjifisherI hope this helps move the issue along a little.
I have implemented the two new hooks in the test module, adding some text to the descriptions. Then I test for the added text. I am having trouble running the tests locally, so I am not sure I got it right. :-(
Please see the comments in the test function.
Comment #24
xjmThanks @benjifisher.
I'm debugging the patch a bit now.
Comment #25
xjmAttached fixes the test and simplifies it a bit. I think it makes sense for this to be a separate test method. I've made it so that different elements are altered at the parent and child levels. I've also used an old trick with state variables to keep the hook from running during other tests.
There's potentially a lot more we could test here but I wanted to keep it simple given that this is a major blocker at the moment for an otherwise simple issue.
Comment #26
xjmAttached sets it up to test both new hooks. I'm not sure the widget we're using is the right choice for testing multiple elements, since it appears that it concatenates a comma-separated list in a single field rather than having separate fields. But this will prove the basics are working either way.
Comment #28
xjmOops. Gave the interdiff a patch extension. At least I can cancel the test again now!
Comment #29
amateescu commentedEdit: this review is for #25.
Not sure why we would include the cardinality as a context, isn't it enough that it is available in the
$elementsFAPI array?Shouldn't we put
$elementsas the argument forchildren()?WidgetBaseInterfacedoes not have aformMultipleElements(), that's a protected method fromWidgetBase.I think this needs to reference
hook_field_widget_multiple_form_alter()instead.Missing a 'multiple' in the example hook name.
The name of this state entry makes it sounds like it's testing the existing widget_form_alter hook, not the new "multiple" one.
Comment #30
xjmThis is cruft that can be removed; I was just making sure I named the other hook correctly. :P
Comment #31
xjmThanks @amateescu
Attached addresses #29.2-5 and #30.
Regarding point 1:
I actually wasn't sure why we were including it in the FAPI array. The cardinality seems a cleaner place to add it. :) Left as-is for now pending further discussion.
Point 6 was already addressed by the previous patch, I think.
Comment #32
tedbow#29.1
I agree with @amateescu that having it in the FAPI array is enough.
re @xjm
Did you mean "The context seems a cleaner place to add it."?
I think though it is already in the FAPI array so we should remove it. So I think removing it from the $context is better.
#29 2-5 were fixed
#29.6
Yes this is now fixed in the patch because the actual hook names are used as the state key.
So other than #29.1 this looks good to me.
Comment #33
larowlanAnother way around this is to use the one-per delta existing hook and add a process callback when the delta is 0. The process callback has access to the complete form by reference
ER in d7 used to do this for Ajax stuff
Would work without any code changes to core
Comment #34
berdirI also think the "can no longer affect" part isn't really true, I don't think this is a regression. In 7.x, influencing the whole widget and not just a single item was much harder as it was not OOP, also for the widgets themself. The only thing that existed was the field behavior constants as used by https://api.drupal.org/api/drupal/modules%21image%21image.field.inc/func... for example.
https://api.drupal.org/api/drupal/modules%21field%21field.api.php/functi... also had delta context, and is called in the loop in https://api.drupal.org/api/drupal/modules%21field%21field.form.inc/funct....
What additionally existed was hook_field_attach_form(), but that is called on the whole form and not just a single field. And in 8.x, you can reproduce that by implement hook_form_alter() and then checking that $form_state->getFormObject() is a ContentEntityForm instance (and not a confirm form, to exclude delete and similar confirmation forms).
That said, I'm +1 on adding this hook, but it might "only" be a DX improvement, not a major bug/regression...
Comment #35
xjmThanks @larowlan, @tedbow, and @Berdir. I know it worked in D7 somehow because I had to use it in a module. :) The delta was still there (and wrong) but the hook did get invoked at both the top and lower level.
Well this still doesn't allow modifying the outside wrapper. So I think the hook is till the way to go.
Yeah I think this was probably a leftover from trying to work around the reduced scope of the hook, so I'll update that if this is what others think is best.
Comment #36
larowlanIt does, a process callback gets the following signature - you have the whole form by reference
From FormBuilder
Comment #37
xjmHere's the patch. (Sorry, wifi fail earlier.) We did confirm that this approach (without cardinality in the context) still works.
I do think the new hook is probably the way to go still.
Comment #38
tim.plunkettThis is one of those things that I see and ask myself "how did we get by without this?"
Which I think we also asked when
hook_field_widget_form_alter()was added (also by @xjm!!)The changes to existing code look good, the added hook looks good, the docs/example look good, and the comparison to the old hook is good.
RTBC!
Comment #39
tim.plunkettComment #40
larowlanPatch looks good, bugger that we have to add another hook, but the only other way around it at the moment is to use some Form API voodoo with process callbacks.
Can we get an IS update here, as the 'remaining tasks' seems out of date.
Thanks.
Comment #41
xjmI've updated the IS and also the CR. Since these are small things, setting back to RTBC. :)
Comment #42
amateescu commentedFixed a couple of things which were not addressed in #37.
Comment #43
xjmAdding #2942097: Deprecate hook_field_widget_form_alter() in favor of hook_field_widget_multiple_form_alter() to the IS which I'd filed previously.
Interdiff in #42 looks good; thanks @amateescu.
Comment #44
xjmSaving credit for the subsystem maintainer reviews and other reviewers.
Comment #45
xjmAlso fixing the "API changes" section to be more specific. :)
Comment #46
xjmPer discussion with @amateescu, @catch, @berdir, and myself, I've renamed the hook to
hook_field_widget_multivalue_form_alter()which I think is a little more clear. (@catch, @amateescu, and myself were in favor of said new name; @berdir was not totally in favor but also said he did not have a strong opinion.)Probably need to let tests finish running in case I missed any bits to update there. I'll also update the CR.
Comment #48
xjmHm yep had one search-and-replace too far. Fixed in attached. Setting straight back to RTBC since this change also had signoff from several folks.
Comment #50
xjmDuh.
But hey, I proved the test coverage works. Let's call that last a test-only patch. ;)
Comment #52
catchCommitted/pushed to 8.6.x and cherry-picked to 8.5.x. Thanks!
Comment #55
ndf commentedThe test added in this issue fails when applying #2915036: Display mode configurations don't get updated with new fields
The test added here fails when, while running the test-suite, entity_form_display are loaded via the entityTypeManager. Note that this is only loading of data, nothing is saved here!
In 2915036 this happens like this
$this->entityTypeManager->getStorage('entity_form_display')->loadMultiple($form_mode_ids);.Issue 2915036 loops over all form_displays after saving/updating to assure correct field settings.
I tried to understand what is happening in this test, but I have a really hard to time understanding it.
Is this testing that a specific creating message in shown on the page?
If someone can give me advice where to take a next step that would be appreciated.