ok, we have some serious problems here, and discussion is basically blocked by being spread over countless of issues.
I'm not even sure whether I understand every issue in detail, so here's the list, plus what I understand:
- #622922: User input keeping during multistep forms
$form_state['values'] vanishes when rebuilding a form. Thus, some elements in a form do not receive a value; especially when the form is submitted + rebuilt via AJAX.
- #634440: Remove auto-rebuilding magic for $form_state['storage']
Form API contains some weird magic around $form_state['storage'], which automatically triggers caching of the form AND a rebuild of the form if not empty.
- #583730: How many ways are there to cache a form?
Too many triggers to cache a form. Plenty of built-in magic that complicates the entire scenario even more.
- #302240: button broken due to fix various problems when using form storage
Broke #type 'submit'/'button' and form rebuilding for multi-step forms.
- #634984: Registration form breaks with add-more buttons
Fields cannot be attached to non-multi-step forms (like the user registration form).
- #367006: [meta] Field attach API integration for entity forms is ungrokable
Clean up all entity-handling forms and make Field API "auto-attach" itself to an entity form. The custom $form['#builder_function'] property needs to vanish, and a lot of weird logic for field_attach_form(), field_attach_submit(), etc. needs to be streamlined.
- #629252: field_attach_form() should make available all field translations on submit
Needs the entity as well as fields in an entity form in a standard location like $form_state['storage']['node'] and $form_state['storage']['fields'].
Comments
Comment #1
yched commentedShort reminder for people new to those issues: the issue with Fields in forms is that any form that wants to include fields needs to be multistep, because of the 'add more' button.
Am I right in stating that the solution for #367006: [meta] Field attach API integration for entity forms is ungrokable will rely on standardizing the use of $form_state for 'entity' forms - or more generally for forms that want to include fields, but that in order to move forward we need to clarify how $form_state and its subparts behave regarding form workflow and cache / rebuild behavior ? Or is this more intricated that this ?
Comment #2
effulgentsia commentedAwesome. Here's my recommendation for how to proceed: first, let's remove all the magic triggered by $form_state['storage'] and just have it be what it says: a place to store information, either within or across page requests of a form workflow. Does #634440: Remove auto-rebuilding magic for $form_state['storage'] accomplish all of that, or is there weird caching magic that also needs solving in #583730: How many ways are there to cache a form? before $form_state['storage'] can be used without side effects?
Then, let's discuss fago's idea of using $form_state['storage'] as THE place from which form constructor functions populate default values. If that flies, it will solve #622922: User input keeping during multistep forms. If it doesn't fly, we'll need to continue on that issue until we have a workable solution.
After that, I think we'll have a framework in place for tackling the remaining issues.
Comment #3
effulgentsia commentedCross-posted with yched, but yes, I think the problem being tackled in #622922: User input keeping during multistep forms is more generic than just FieldAPI. It should be possible to add "add more" buttons that work with and without js to ANY form. And I think the solution will require either something along the lines of what's being proposed in that issue or fago's recommendation to build all forms using $form_state['storage'].
Once that's in place, I think #367006: [meta] Field attach API integration for entity forms is ungrokable can be focused on FieldAPI specific details regarding entities.
Comment #4
rfayI certainly agree with effulgentsia that magic behavior based on $form_state['storage'] should be eliminated. This has been a long-term hazard.
Comment #5
fagoThanks sun for providing this overview! Indeed a lot of issues..
Well as already mentioned I think we should start using $form_state['storage'] as the place for holding data regardless it's a multistep form or not. Then it can become one anytime - this should fix mostly all of this issues. See http://drupal.org/node/622922#comment-2276812 for a more detailed post why I think this is the right solution.
>Does #634440: Remove auto-rebuilding magic for $form_state['storage'] accomplish all of that, or is there weird caching magic that also needs solving before $form_state['storage'] can be used without side effects?
Simplifying the caching logic is a separate thing, however then another possible problem is left. The form constructor is called differently when the form is set to be cached - which is done automatically once there is #ajax stuff in it or once the form is rebuilt (then caching is activated one step later).
So this might cause problems when the original form relies on the form constructor to run to put stuff in the $form_state for it's handlers (without storage) or includes files. Ideally the form would always behave the same way here too.
To achieve that we could activate $form_state['cache'] by default. However then we would have to ensure that the cache isn't overwhelmed with useless stuff from forms appearing on nearly any page like the user login block or search form. Setting 'no_cache' for those forms would be a possible way to go. Or we just stay with this difference and try to educate developers to write their forms right using form storage to pass around things and/or develop with $form_state['cache'] enabled.
So keeping values when rebuilding would solve the problem for the form values, but still the form behaves different thus forms relying on the form constructor to include or put stuff into formstate without storage would break once 'cache' is activated.
Comment #6
plachComment #7
sunSeems like previous discussions already lead to some kind of conclusion. But still, I think we should discuss the macro-level picture first -- i.e. now that we know the problems, how exactly do we want to fix them, and, does that make sense for developers in the end?
I don't think we want to introduce or live with any magic. Contrary to that, I'd say we want to remove the entire magic.
If you want to store something in 'storage', you can do so. But if your form + storage should be cached, then, man!, you want to enable caching, don't you?!
Or in other words: We want a clean + simple pattern. Enable IT. Get IT. Disable IT. Don't get IT.
Every #process and every #validate and every #submit handler, and also the original form constructor, can alter the $form_state properties. So if you want to attach fields to a form that is normally uncached and doesn't hold any storage, then you can populate it.
The 'no_cache' property (and related 'no_*' properties) should be used in edge-cases only, because they override another property. I'd consider them only for contributed or custom modules that absolutely need to override the default behavior. You can always toggle the 'cache' property. You only need to do the forced override to prevent anyone from enabling 'cache'.
This part I didn't understand - could you elaborate? Or do you mean the $form_state['input'] / $form_state['values'] issue we're currently tackling over in #622922: User input keeping during multistep forms?
If you'd ask me, then I'd say we already have some first, clear issues to move forward:
Comment #8
chx commentedform_state['storage'] triggers caching because you only want to use form_state['storage'] to persist state from one step to another. So if you have storage then you surely wanted to rebuild. I am not exactly sure whether this is the magic you want to remove and if yes then what we would achieve.
Comment #9
sun#634440: Remove auto-rebuilding magic for $form_state['storage'] will remove the rebuilding magic, not the caching.
Whether it is a good idea to also remove the caching magic when 'storage' is populated is a different question. In general, I hate magic. Because the consequence is that you cannot undo the magic behavior, since it is automatically done by API internals. But it might not be worth to remove it here.
Comment #10
rfayI want to mention #386678: Using/setting form_state['storage'] in one form on a page prevents any other form on page from posting ($_POST is destroyed) here. This is a D6 issue, and I have tested but not studied the issue on D7 and it seems not to manifest itself in the same way in D7 (or to have been resolved completely). However, it's an example of the kind of magic that bites you when we're done. I'll plan to write a test for this case.
Comment #11
rfayLet's make a commitment that each of these edge cases/problem issues gets good tests before being committed.
Comment #12
fago>The form constructor is called differently when the form is set to be cached
When caching is activated the form isn't constructed again when the user submits its data. Thus when the form isn't cached it's working to put data in $form_state without storage so that validate/submit handler can access. Once the form is cached, the constructor isn't run before and $form_state misses the values the handlers rely on. They same problem may occur when the constructor is including files needed later on...
@Whether it is a good idea to also remove the caching magic when 'storage' is populated
When there is no rebuild, nothing will be cached. The rebuild is really triggering the caching (in regard to storage), so the patch already removes this magic by removing the rebuild. The "magic" left is that a rebuild enables caching. But that's necessary so that the storage is available during multiple steps/rebuilds.
Comment #13
sunStatus update / summary:
Next steps:
Let's focus on those first, then tackle the rest.
Comment #14
bjaspan commentedsubscribe
Comment #15
effulgentsia commented@rfay: Re #10: That particular problem has been fixed for D7 by using $form_state['input'] instead of $_POST, thereby preventing a rebuild of one form from having an affect on another form on the same page. However, the rest of what's been said in relation to #634440: Remove auto-rebuilding magic for $form_state['storage'] is true, and I'm glad it landed.
Comment #16
fagonew idea:
* http://drupal.org/node/622922#comment-2300826
* -> #641356: Cache more of $form_state when form caching is enabled
comments wanted! :)
Comment #17
sunI'm not sure what we gain from the patch in #641356: Cache more of $form_state when form caching is enabled, because it is only changing how the values in $form_state are cached, and, I'm rather opposed to do such a change at this stage of the release cycle.
Comment #18
sunFYI: The latest patch in #629252-19: field_attach_form() should make available all field translations on submit tries to move $object in fieldable forms into $form_state['storage']['object'] as dedicated data source for field_attach_*() functions.
Comment #19
fago>I'm not sure what we gain from the patch in #641356: $form_state should stay alive during the whole form workflow, because it is only changing how the values in $form_state are cached, and, I'm rather opposed to do such a change at this stage of the release cycle.
"better backward compatibility" - we don't have to move everything into storage but we can stay using variables already in $form_state, e.g. $form_state['node']
>I'm rather opposed to do such a change at this stage of the release cycle.
I agree it's late for those changes, but I think that's a problem for each solution fixing the problem of #634984: Registration form breaks with add-more buttons in general - thus making forms not break when altering it to be multistep.
Also this fix works fine with every single form in core - that's because it makes $form_state working as it's expected by devs anyway. You put data in there and expect it to be still there later on.
Comment #20
sun.
Comment #21
sunStatus update:
This is most likely due to the fact that nowhere is documented what is cached when a form is cached. (see explanation)
To move further further properties and stateful information away from $form into $form_state and still warrant speedy AJAX operations (and make them properly work in the first place), we need to move any form processing and form validation logic out of form constructor functions.
Interestingly, this patch contains a @todo that is in line with fago's proposal of #641356: Cache more of $form_state when form caching is enabled to some extent.
Please note that we have a very nice target to work on now.
Comment #22
Crell commentedSubscribing. I'm trying to develop an entity module right now and running head first into FAPI WTF land. There's stuff going on here that I can't even begin to comprehend, mostly the workflow of the $form['#builder_function'] stuff.
Comment #23
sunI'm marking this issue as fixed in favor of the issue tag queue:
http://drupal.org/project/issues/search/drupal?issue_tags=D7%20Form%20AP...