Problem/Motivation
In order to alter the properties of a field widget form element defined by another module, modules must loop over every field of every form in either hook_form_alter()
or hook_field_attach_form()
, searching for the correct field or widget type. This adds unnecessary overhead and makes field and form customization far more complicated than it needs to be. For example, a module enhancing the functionality of core taxonomy fields must check every form for taxonomy field widgets.
Proposed resolution
Create a hook_field_widget_form_alter()
(with widget-specific variant hook_field_widget_WIDGET_TYPE_form_alter()
) to allow the widget form to be altered directly.
Remaining tasks
None.
Create new hooksDoneVerify functionalityDoneDocument new hooksDoneReview by field API maintainerDoneReview final version of patch in #1204230-30: Missing hook_field_widget_form_alter()DoneSign-off by field API maintainerDoneReview patch #33Done
User interface changes
None.
API changes
Minor, backwards-compatible addition only. No existing code will be affected.
Two new hooks will become available:
hook_field_widget_form_alter(&$element, &$form_state, $context)
hook_field_widget_WIDGET_TYPE_form_alter(&$element, &$form_state, $context)
Original report by @j0rd
I'm trying to do a simple task.
I've created a field, and I want to put a custom validator on it. I'm using a textfield, and want to make sure for this particular instance, it's a URL link.
Thing is, I might add this field into more than one entity/bundle or what ever they are called these days. So I'd really like to attach it to the field instance (aka field_name) instead of the normal form_validation. This seems like the best place for it to be.
Unfortunately there's no hook to get at this. The closest I can get is hook_field_widget_form(), but unfortunately this is only called with the module that defines the widget...so as far as I can tell...I'm at a dead-end with out hacking core. There was some promising looking module_invokes in _field_invoke(), but again, those fall short.
I also think it would be a wise idea to assist developers to create a field_FIELDNAME_validator() type shortcut to a hook like this.
Maybe there's a way to do this, that I don't know about....but right now I'm stuck. I tried looking this up and only found two other people with the same problem I'm having.
So, is this something I can already do, but can't figure out? Or is this not important? Or is this something we'd like to add into core to ease developers lives & code portability.
PS. I know I could define my own widget to get my own validation...but IMHO, this is slightly overkill.
Comment | File | Size | Author |
---|---|---|---|
#36 | field-widget-form-alter-1204230-36.patch | 6.66 KB | xjm |
#33 | field-widget-form-alter-1204230-33.patch | 6.66 KB | yched |
#33 | interdiff.txt | 6.02 KB | yched |
#30 | field-widget-form-alter-1204230-30.patch | 5.06 KB | xjm |
#28 | field-widget-form-alter-1204230-28.patch | 5.07 KB | xjm |
Comments
Comment #1
j0rd CreditAttribution: j0rd commentedSome references
Comment #2
xjmAnother possible (but as-yet missing) solution would be a hook_field_validate_alter(). This would be especially useful in my use case--I need to alter the list of terms a user may submit in term reference fields of any widget on any entity type, and I want to be able to enforce that in validation in a portable way.
Comment #3
sunComment #5
xjmLooks like it needs to be
$instance['entity_type']
.Comment #6
xjmOops, misclick.
field_multiple_value_form()
doesn't get the entity as an arg apparently?Edit: the only place I found the entity data in
field_multiple_value_form()
was inside$form
. Would it needed to be added as an argument?Comment #7
xjmComment #8
xjmFor anyone else struggling with this, here's the sort of workaround I'm implementing atm. (This is half-baked and needs some checks for whether stuff is defined, etc., but you get the idea.) Looping through everything on every form is definitely less than ideal, but it's the best I've come up with at present given that we want to handle any entity types. I actually don't even know if it will work for non-nodes yet, but at least the possibility is there...
Edit: improving it a little.
Comment #9
xjmCouple folks on IRC pointed out we don't need to pass
$entity_type
and$entity
as separate arguments. So here's a reroll without those in$context
.Comment #10
xjmJust confirmed that this patch applies to 8.x and 7.x, and allows me to alter the widget form very cleanly. E.g., with the following:
Brilliant!
Comment #11
xjmTo add this, we would also need to add the hook documentation in
field.api.php
. Looking into this now.Comment #12
xjmAttached patch includes hook documentation.
Comment #13
sunIs this info available through other means or...?
The additional @see is not necessary here.
"The field widget form element as constructed by ..."
17 days to next Drupal core point release.
Comment #14
xjm#13: The entity is available in
$form_state['build_info']
, and the entity type is both there and in$instance
IIRC.I'll fix the other documentation issues, thanks.
Comment #15
xjmSame as #12 but with hook documentation corrections.
Comment #16
catchLooks good to me at first glance, we usually put @see at the bottom of doxygen rather than the top.
It feels like $form/$form_state are going to be pretty different depending on where the widget is, is that a feature or not?
Comment #17
xjmThis does seem like a possibility, but the hook is not introducing new behavior that is not already present in
hook_field_widget_form()
, which gets all of the above and then some as arguments:hook_field_widget_form(&$form, &$form_state, $field, $instance, $langcode, $items, $delta, $element)
Perhaps we should consider changing the signature to match this, though.
Comment #18
catchIf it's not new behaviour that seems fine.
Matching signatures wouldn't do any harm.
Comment #19
sunThe signature cannot be matched further and should not I think -- the only difference is in $form vs. $element, but hook_field_widget_form() actually produces $element, not extending $form, so we're passing on $element to the _alter().
Comment #20
xjmChecked with sun about this. For future reference: To use drupal_alter(), we need the first argument to be the thing we're altering--the field widget form element, in this case--and then up to two additional parameters, the last of which is an associative array containing as many other parameters as we want to feed the alter hook.
$form_state
is the next most reasonable thing to alter by reference, so it makes sense to have that as the second argument.So, the signature is correct as it is.
Comment #21
sun@yched or another Field API/module maintainer needs to sign off this patch.
Comment #22
droplet CreditAttribution: droplet commentedWill it backport to D7 ??
Comment #23
catchShould be IMO, tagging.
Comment #24
yched CreditAttribution: yched commentedshould be "the field instance structure"
Nitpick : we might want to give an example discriminating on a specific widget type (i.e checking $context['instance']['widget']['type']). Sounds more "real life".
This is reusing the $context var built for the hook_field_widget_properties_alter() call a couple lines above. I'm not too fond of this, let's please build a fresh, dedicated $context var.
This allows two variants :
a "targeted" hook_WIDGETMODULE_field_widget_form_alter(), and a "generic" hook_field_widget_form_alter().
- I think the core practice is to put the "targeted" variant second.
- The "targeted" variant needs to be documented as well.
- I don't think targeting by WIDGETMODULE is the right approach here. The same module can provide several widgets that are structured very differently. I'd say we'd rather want to target by widget type. As a developer, I know which widget I want to alter, I don't care which module provides it. Also, that fits with the "widgets as plugins" direction we'll want to push for if/when the WSCC initiative comes up with a plugins system.
Also, the alter hook doesn't receive the other params that the original hook_field_widget_form() 'callback' receives ($langcode, $items, $delta) ? I'd think those should b added to the context ?
More generally on the feature : I'm not opposed to this, but the symmetry between widgets and formatters raises the question of a similar hook_field_formatter_view_alter() hook on the formatter side - which we cautiously avoided so far for performance reasons (a listing page can call 100s of formatters, and field rendering is already quite slow).
14 days to next Drupal core point release.
Comment #25
xjmAttached patch:
@see
to the bottom of the docblock as suggest by catch in #16.$properties_context
and$form_context
for the respective alter hooks.langcode
,items
, anddelta
to the context (as well as adding the entity type explicitly for consistency, since this is the pattern when the widget is built infield_multiple_value_form()
.There are two points I haven't addressed:
To clarify (since you quoted two lines). Should it read:
I am not sure how to address this.
Comment #26
yched CreditAttribution: yched commentedThx @xjm.
- $properties_context and $form_context :
I think both can be called $context. Those are one-off variables anyway, created just for the sake of the alter call.
- "field: The field structure. / instance: The field instance structure."
Yup :-)
- variants :
I'd suggest
Comment #27
xjmAh, it makes sense now. I agree this is a much better hook. In that case I will move the widget-specific example to that hook doc, and act based on field type in the generic one.
Comment #28
xjmAttached incorporates suggestions from #24-26.
Comment #29
xjmOops, some trailing whitespace.
Comment #30
xjm#28 but without the trailing whitespace.
Comment #31
xjmComment #32
xjmSame patch applies to D7 fine. I confirmed that both the generic and specific hooks work as advertised.
Comment #32.0
xjmUpdated issue summary.
Comment #32.1
xjmAdded direct reference to most recent patch.
Comment #33
yched CreditAttribution: yched commentedThanks @xjm.
Polished a little further :
- Changed hook_field_widget_NAME_form_alter() to hook_field_widget_WIDGET_TYPE_form_alter()
- Adjusted the $context elements to match the args received by hook_field_widget_form(). The $form_state stays it's own parameter, as it's alterable.
- Adjusted the @see references in the phpDocs.
It's now up to someone else to RTBC, though :-/
Comment #33.0
yched CreditAttribution: yched commentedTypo and clarity.
Comment #34
sunThanks!
Comment #35
xjmNoticed a typo (follwoing). I'll reroll quicklike. Yay green!
Comment #36
xjmComment #36.0
xjmupdate status
Comment #36.1
xjmClearer indication that this is a small API addition, not a change.
Comment #36.2
xjmClearer.
Comment #36.3
xjmUpdated tasks.
Comment #37
xjmCrossposting some issues that reference this one:
#982764: Document how to upgrade CCK field modules that used hook_widget_settings_alter
#672526: hook_widget_settings_alter() is missing in D7 (and D8) and several D6 modules cannot be ported to D7 because of this.
#1212142: How to alter widget type programmatically on a per-form basis?
as well as several in TAC.
Comment #38
goron CreditAttribution: goron commentedIn case it matters, I'm using this patch and the hooks work well.
I'm wondering - do you know when these changes will be added to a D7 release, or in which release they'll be included?
Thanks.
Comment #39
j0rd CreditAttribution: j0rd commentedI don't think this patch has many eyes on it. Tweet / Google+ about it and see if you can get more people to test it and hopefully it'll help get this in faster once more people say it works as expected.
Comment #40
mhahndl CreditAttribution: mhahndl commented@j0rd: This patch has many eyes on it - i visit this page daily. Missing hook_field_widget_form_alter() is a D7 release blocker for several modules, so this patch is important for a lot of people.
@xjm: Thanks for the patch. It works as expected.
Comment #41
alexbern CreditAttribution: alexbern commented@j0rd: We are very eager to see this in core as well. +1 from my side
Comment #42
xjmThis is somewhat blocking a stable release of TAC at present.
Comment #43
goron CreditAttribution: goron commentedIt seems that this patch does have some eyes on it, and has been tested a few times. It is also necessary for a module I would like to release.
I'm not familiar with the process of getting patches committed to core. Is there anything we can do to speed it along? I personally don't many people in my twitter/google+ who I can share this with.
Comment #44
xjmMarked #1212142: How to alter widget type programmatically on a per-form basis? as duplicate of this issue.
Comment #45
catchMoving to major since it's blocking stable contrib releases.
Comment #46
webchickThis looks like a pretty straight-forward oversight in the API. I'd been holding off on this patch while we got our bug counts down, but that seems to have been accomplished in the last couple of weeks, so cool.
Committed and pushed to 8.x and 7.x. Thanks a lot.
Comment #48
xjmComment #48.0
xjmCorrected name of specific hook in summary.