Except for the node form which has hook_node_submit() current entity forms in core cannot be extended in a clean way, such that a form element for editing a custom entity property is added (without using the field API).
Additionally some core entities implement $form['#builder_function'] - thus reusing this function for building the entity after a submit is possible. This feature is only needed in order to use the entity form as part of a wizard or split it up into a wizard-like form - regular form rebuilds work fine without it. Therefore I think the current situation of having not all entities support is fine, whereas of course having this opportunity in any entity form would be nice.
-> Thus I don't think the second point is something that is important to fix, but I'd say the first is a must. As I think Drupal 7 really should have properly extensible entity forms, I add the major tag.
To start the discussion I try to summarize my thoughts on the first point.
Possible ways to make entity forms extensible:
- Introduce hooks.
Just as the node form does. Add hooks for validation + extracting form values (hook_entityType_submit). - Rely on FAPI
- Provide both ways
We could also provide both ways at the same time.
As described in #735800-92: node form triggers form level submit functions on button level submits, without validation FAPI lacks a way of invoking the form default behavior. When a button overrides validate + submit it could need way to invoke the overridden default behavior. That way instead of implementing a hook modules could just add their validation + submit stuff as regular form validation/submit handler. This works in particular fine for #validate, but in order to work fine for #submit too the entity submit phase needs to split up into two steps/submit handler: One for updating the $entity in form state and one for actually saving the entity. Ideally the latter is invoked via a button submit handler, so the form default submit handlers are just caring about updating the entity - thus they could be easily reused. For that a function just invoking the default form submit handlers could serve as #builder_function.
This is also similarly implemented by the node form for #submit, but not in a very clean way.
Personally I tend to think the FAPI option is the way to go.
Disadvantages:
>Hooks:
* If we add hooks for it, there are still the FAPI #validate and #submit handler which could be partially used too. Thus we are just adding another possibility to do it - one that is working properly for any case though.
* Hooks act globally on entity level. However entity forms might not be extended only in some cases, thus even if the added form element is not there, the hook is invoked - which is no problem, but not very nice. If an entity starts to do several different kind of forms though, this becomes confusing.
* What's the point of having a hook when the actual action to be taken acts on the form level?
>FAPI:
* Using form-level #submit handlers for updating the entity and only because of that moving the handler for saving to the button level could maybe be called unclean?
>Both:
Having both ways is probably just confusing people.
Advantages:
>FAPI:
* Only one way to do it. The less ways there are, the less people are confused.
* Providing a way to invoke default FAPI handlers would be a nice improvement for non-entity forms too.
* When we have FAPI file inclusion support as done by #561226: Forms (elements) need to able to specify include files to be loaded for building, the function callbacks could be easily put into any include file. That's not so simple for hook implementations (we could just add one fixed file 'group' for the hooks via hook_hook_info()).
| Comment | File | Size | Author |
|---|---|---|---|
| #36 | extensible-reroll.patch | 8.69 KB | fago |
| #32 | extensible.patch | 8.69 KB | fago |
| #30 | entity_form.patch | 9.04 KB | fago |
| #25 | extensible.patch | 1.85 KB | fago |
| #19 | extensible.patch | 2.16 KB | fago |
Comments
Comment #1
rfaysubscribe
Comment #2
yched commentedsubscribe :-)
Comment #3
sunWe need to do both 1) and 2).
And as long as there is no new Form API facility that would allow us to bundle/group any entity submit/update callbacks to ensure at least some sanity in the order of #validate and #submit functions, we should continue the hook_ENTITY_submit() pattern. At least for D7.
Comment #4
effulgentsia commentedYeah, I still find this somewhat suspect, and am concerned about rushing something like that into D7. Currently, we only do this for node forms, and that's cause that's how we did it in D6, but I'm not convinced yet this is a pattern we want to spread. Note, I'm not opposed to adding a form_execute_default_handlers() function per se, but I'm not so sure we want to make the updating of an entity be the form-level #submit. Please see Barry's comment and my reply about this issue of using a form-level #submit in this way. For D8, we can have time to think about things like #pre_submit or #default_submit, but changing the conceptual relationship between form-level #submit and button-level #submit for D7 doesn't sit quite right with me.
Therefore, I think my preference is hook_(comment|user|taxonomy_term)_(validate|submit)(), though I agree that this is unfortunate, because since multiple forms can be created for the same entity type, and even the main form can be altered, it means every implementation of such a hook will need to have an if statement that checks whatever assumption about the form it needs to check, as menu_node_submit() does.
I'm definitely still open to other ideas, if we think they're do-able for D7.
Comment #5
effulgentsia commentedI'm adding the FAPI challenge tag to this, because maybe the right solution will be to add more FAPI properties to have finer control of validate|submit handler execution. Probably not, but maybe.
Comment #6
Frando commentedWhat about making
#builder_functionbe an array?Then forms that extend an entity form and add a custom property can add their builder function in their form_alter:
In my_custom_builder_function(), the module would set its properties in $form_state['node'] based on $form_state['values'].
In node_form_submit() and node_form_build_preview(), in place of
$node = $form['#builder_function']($form, $form_state);we'd have
and in form.inc
node_form_submit_build_node() can stay as is now, except it wouldn't return $node anymore.
That would be an API change, but we changed the API of #builder_function a couple of days ago anyway, and I don't think it's used a lot in contrib as of now.
Comment #7
fagoad #4:
Agreed. Both ways aren't ideal yet, but I don't feel like introducing a new FAPI property is the right way to go. Having a #validate + a #submit makes sense. Ff we need an extra step for entity forms we should implement it there, but not generally on FAPI level (if there is no cause to do so).
ad #6:
I like this idea.
I'd vote for removing _function, as we its an array of callbacks now just as #submit and #validate. Thus #builder should be fine. But note - as #builder needs to get manually invoked, it's no real FAPI property. Therefore form_execute_builder_functions() should be something else, e.g. entity_form_build_entity() whereas we could add the existing entity_form_submit_build_entity() there as entity_form_default_builder() or such? If we want, we could even support BC for #builder_function by pointing it to entity_form_build_entity(). But with #builder I don't see a need for #builder_function, as I don't think it's used anywhere I'd vote for dropping it now.
Comment #8
effulgentsia commentedI could go along with #6/#7 if someone wants to roll that patch. I think the basic premise here is that form-level #validate is already what we want. It's not often there's button-level #validate, and if there is, and if it wants to invoke the form-level ones before or after calling its own, that makes conceptual sense. I think what I'm stuck on is making the form-level #submit of entity forms incomplete by only being the thing that builds the entity, but not doing anything with it: other than node forms, all our forms have form-level #submit that does the complete primary action of the form (i.e., for entity forms, saving the entity), and then we use button-level #submit for all the "other" buttons, but not the "primary" one. And that's why we have a new property like #builder_function, or whatever we may want to change that to, for building the entity independent of what we then do with it.
While we're still in the brainstorming phase, here's another option to throw into the mix. Leave #builder_function alone (if we care at all about BC here) to be used by the module that defines the form, and add a new #entity_submit property for modules that implement hook_form_alter() to extend the form. See patch.
Comment #9
sunGood ideas. I'd vote for #entity_submit, using Frando's proposal of functions to invoke, i.e., #8 and #6 combined, merging #builder_function into #entity_submit.
Comment #10
moshe weitzman commentedAnyone available to push this along?
Comment #11
fagook, here is a patch the makes the comment form extensible, so we can discuss the approach on the example of comments.
Attached patch:
Opinions?
Comment #12
fagoHere is an updated patch that merges invoking #builder into entity_form_submit_build_entity() - just as #8 does. That way the helper does everything properly and modules just need to invoke entity_form_submit_build_entity() from their #builder_function, passing the edited entity to it.
Comment #13
fagoand the patch...
Comment #14
fagoOpinions on #13? We should fix this rather soon such that contribs finally can follow the core-pattern for implementing entity forms.
Comment #15
sunI guess that works - but #builders absolutely needs to be plural.
Are all core entities invoking entity_form_submit_build_entity() already?
Powered by Dreditor.
Comment #16
fago>Are all core entities invoking entity_form_submit_build_entity() already?
Yep.
>I guess that works - but #builders absolutely needs to be plural.
Makes sense.
Comment #17
sunLooks good to go for me.
Comment #18
yched commentedSorry, minor :
typo / missing word ?
47 critical left. Go review some!
Comment #19
fagook, I simplified this sentence.
Comment #20
yched commentedThks, back to RTBC.
Comment #21
marcingy commentedChanging to major as per tag.
Comment #22
mustanggb commentedTag update
Comment #23
fago#19: extensible.patch queued for re-testing.
Comment #24
fagoAny update on this?
Comment #25
fagoThe trailing whitespace has already been removed. Re-rolled the patch without that whitespace fix.
Comment #26
sunComing back to this issue after a while, I'm still happy with this patch, but I first wondered
"ok, what's a 'builder' again?"
"...and, is that a Form API property, or to which API/module does it belong?"
If that other property wouldn't be called #builder_function already, then I'd really have to insist on proper namespacing, i.e., #entity_builders.
Powered by Dreditor.
Comment #27
dries commentedI'm also confused by '#builder', especially because we already have $form['#builder_function']. Gotta say, something doesn't feel quite right.
Comment #28
sunAFAICS, #builder_function was only left for backwards compatibility. If @Dries in #27 was a "go, break those APIs!", then we can merge #builder_function + #builders into a new #entity_builders
Comment #29
dries commentedPersonally, I think we can still break APIs while we're in ALPHA. We don't support HEAD2HEAD database updates either, so I don't see why we would treat code entirely different. So, let's break the API if (and only if) that makes it easier to understand. Once we're in beta, this will obviously change.
Comment #30
fagoOk, if that is an option I'd suggest to completely scratch #builder_function - it just made things unnecessarily complicated. #builder_function is only needed for doing wizard-like forms across different entity types - I'd say a rather rare scenario. Then we even have complete support in core, as it's not there for vocabularies or user accounts.
Instead let's use the naming convention FORM_NAME_submit_build_ENTITY_TYPE() which is mostly already established in core - that way we can scratch the ugly #builder_function property but still keep the option of the above mentioned rare use case. Attached patch does so + renames #builders to #entity_builders.
Comment #31
sunThe property should still be plural -- similar to #theme_wrappers
Shouldn't the function name pattern rather be
MODULE_form_submit_build_ENTITYTYPE()
?
At least, that would make sense to me, and is the case for all functions here, except taxonomy's, which repeats "term".
Powered by Dreditor.
Comment #32
fago@builders: Makes sense, fixed that.
@MODULE_form_submit_build_ENTITYTYPE()
I don't think so. The build function is specific to the form not to the entity. If - theoretically - there are multiple entity forms for a single entity type it might need multiple different build functions.
Comment #33
sunThanks!
Comment #34
fago#32: extensible.patch queued for re-testing.
Comment #36
fagoI don't see how this should influence the javascript system/test - looks like a fragile test to me. -> Let's re-test it.
I re-rolled the patch to apply without offset.
Comment #37
fagoeverything fine, so back to RTBC.
Comment #38
fagoThe patch renames
taxonomy_form_term_submit_builderin order to match the naming convention as discussed in #30. Thus it is a function rename + finally totally deprecates $form['#builder_function'], so it is a small API change. Anyway it should be committed before the beta, so that we have finally figured out the way to do entity forms in core and contribs can follow that.Comment #39
sun#36: extensible-reroll.patch queued for re-testing.
Comment #40
sunAlthough badly needed, this is D8 material according to the rules (I had to learn today).
Comment #41
fago?? According to which rules?
* This is a bug that certainly needs to be fixed. It's impossible to extend an entity form (not being the node form) without using fields.
* Dries even encouraged us to finally scratch $form['#builder_function'] before the beta in #29.
Comment #42
catchPer #29, this needs to be ruled in or out before we get to beta, adding tag.
Comment #43
effulgentsia commentedGiven we're past beta1, and about to be in beta2, can #36 be modified to still be in consideration for D7?
Comment #44
effulgentsia commentedAlso, another issue related to our lack of consistent and mature generic entity / entity-type-specific APIs: #949576: Missing hook_entity_view() and hook_entity_view_alter().
Comment #45
sunThis needs committer feedback, so RTBC. Also, this patch was ready a long time ago, and the sanity it introduces/restores is badly needed.
Comment #46
andypost+1
Comment #47
sunThis patch is required for the http://drupal.org/project/title project (which turns entity labels/titles into fields, for many use-cases, but primarily to make them translatable).
Title module needs to act before field_attach_submit() is invoked -- which is exactly what this patch here does.
Comment #48
sun#36: extensible-reroll.patch queued for re-testing.
Comment #49
webchicksun graciously spent some time with me in IRC on this patch.
So first off, I was concerned because Dries's comment back in #29 talked about breaking APIs pre-alpha. We're now way post-alpha, and even post-beta, really. But sun explained that the #builder_function property is a core-only construct and the only contributed modules that would be affected would be those actively overriding one of these core properties. For confirmation, I grepped the latest checkout of Drupal Commerce, which is the project I'm aware of making the fullest use of entities, and there's no #builder_function anywhere to be found. So I think this change is ok, BC-wise.
What I don't like about this patch is #entity_builders effectively creates an alternate hook_form_alter() for entities only. I still don't really understand why that's needed. But since Dries didn't raise this as a concern in his review, and since this is the last issue as part of #367006: [meta] Field attach API integration for entity forms is ungrokable to resolve in D7 and has buy-in from all the right people, I guess we'll go with it, and clean this whole mess up later in D8.
Committed to HEAD. Thanks!
Comment #50
rfayIs the only API change in #38?
Please explain the impact of this change on existing calling code and let me know if it should be announced.
Thanks!
Comment #51
fagoGreat to see this one finally getting in, thanks sun + webchick!
ad #49:
Yep, #builder_function is obsolete and I've seen no contrib setting it either. #entity_builders is no alternative to hook_form_alter(), in the contrary it is required in order to be able to properly use it on entity forms as you need a way to update the entity based on the form values.
ad #50:
Yes #38 + the API addition of
$form['#entity_builders'], which allow modules extending entity forms to update the entity based upon their form values.