fix various problems when using form storage

fago - August 31, 2008 - 12:41
Project:Drupal
Version:6.x-dev
Component:forms system
Category:bug report
Priority:normal
Assigned:Unassigned
Status:needs review
Description

Currently $form_state['storage'] is intended to be set in a #submit callback. So when creating a multistep form you can put your stuff into $form_state during the form build, then populate the storage during #submit.

This works fine, but when you start to add some #ahah stuff to the form, #cache will be set and the form will be cached when it's loaded the first time. As an affect the #submit callback is called
* without an initialized $form_state, as the form got cached through #cache
* without a form storage, as it wasn't set yet
So there is no way to make use of the $form_state apart from setting $form_state['storage'] during the form build. However currently this isn't intended and doesn't work well, because when you do it
* the form is immediately rebuilt even when it's loaded the very first time
* $form is written two times into the cache, once due to the storage and once due to #cache

To solve this I'd propose support setting the form storage during form build and fix the two mentioned issues. If there is an agreement that this is the way to solve it, I'd do a patch.

Note: This issue applies to 6.x too.

#1

fago - September 1, 2008 - 10:14
Title:form storage doesn't work well on cached forms» fix various problems when using form storage

Furthermore there is another related issue: When the storage is already set and there is a validation error, the form is rebuilt nevertheless! So when one uses a multistep form and saves stuff into the storage with the submit callback, then in this case the submit callbacks are skipped due to the validation error and the form is rebuilt without an updated storage, so any changes are lost.

So I've done a patch which fixes these problems:
* It makes sure the form storage lets the form rebuild only when the form has been submitted and there are no validation errors.
* It also saves the form storage to the cache table when #cache is activate by #ahah properties or something else.

These simple fixes now make it possible to
* put your thingie to edit into the form storage during the initial form build
* to update your thingie after each step out of a #submit callback
- which I'd consider as typical usage.

Furthermore now it's no problem to use the form storage together with #cache.

Note that I've tested the changes with a complex case - the rules admin UI for d6. It's a multistep form using storage which might have some #ahah properties in there - with this patch the form runs fine!

It applies to d7 and d6.

AttachmentSize
drupal_form_multistep_troubles.patch 1.18 KB
Testbed results
drupal_form_multistep_troubles.patchfailedFailed: Failed to apply patch. Detailed results

#2

fago - September 1, 2008 - 10:16

I think it would make sense to write a test for a multistep form using storage and #cache in d7 - however I'm not sure how to do it. Is there a way to add a testcase-module containing such a form just for testing?

#3

Damien Tournoud - September 1, 2008 - 10:38

I just created a book page about our mock modules. Please see http://drupal.org/node/302577

#4

fago - September 1, 2008 - 13:42
Status:active» needs review

ah thanks!

ok, so I've written some tests using a mock module. The module provides a multistep form using storage and optionally #cache. Then the tests ensure that
* the storage properly stores the values
* the form builders aren't called too often
* the storage keeps working, when #cache is activated
* the form values are kept when an validation error occurs.

attached is a patch containing *only* the tests. Make sure to have the patch from #1 included or some tests will fail.

AttachmentSize
drupal_form_storage_tests_7.patch 5.91 KB
Testbed results
drupal_form_storage_tests_7.patchfailedFailed: 7240 passes, 3 fails, 0 exceptions Detailed results

#5

Robin Monks - September 1, 2008 - 14:13
Status:needs review» reviewed & tested by the community

Test patch and issue patch apply file, and the FormsAPI simpletest runs correctly (88 passes, 0 fails, 0 exceptions).

Robin

#6

moshe weitzman - September 2, 2008 - 13:08
Status:reviewed & tested by the community» needs review

@Robin - please slow down. Significant FAPI changes need review from Eaton or chx.

#7

chx - September 2, 2008 - 18:25

The second hunk is a bugfix -- you can have storage populated from a previous step and then a validate error will get the form rebuilt still. Whether we want the builder to be able to populate storage I am less sure. I am open to what Eaton says :)

#8

fago - September 2, 2008 - 20:45

thanks chx.

>Whether we want the builder to be able to populate storage I am less sure. I am open to what Eaton says :)
I'd call this a bug making $form_state unusable, as it requires one to use $form for storing values instead when caching by #cache should work too.

Anyway when putting stuff into the storage during build is not supported, the first hunk makes no difference either. It's unnecessary to pass NULL to form_set_cache when there is no storage.

#9

dropcube - September 17, 2008 - 15:49

I had posted a similar patch in other issue #291939: Multistep forms should not be rebuilt if there are validation errors. because I did not found this one.

Making sure the form storage lets the form rebuild only when there are not validation errors fixes the bugs reported and work Ok form me.

Anyway when putting stuff into the storage during build is not supported

Could you explain why this is not supported. I have some use case where is required to put stuff into the storage during the form builder function. Is there an alternative way to do this?

#10

fago - October 4, 2008 - 12:29

>Anyway when putting stuff into the storage during build is not supported, the first hunk makes no difference either. It's unnecessary to pass NULL to form_set_cache when there is no storage.

Anyway, I think putting stuff into storage during build *should* be supported. Currently it's not, but nobody knows. It's not documented and as it's useful people do - yes, I have seen several cases where people do so. Then some days ago I held a copy of the latest pro drupal development book in my hands - even the multistep form example in there sets $the storage in $form_state during the form build!

@dropcube: What happens when you populate the storage during form build currently?
* the form is immediately rebuilt even when it's loaded the very first time. So the builder runs 2x when the form is shown the first time.
* When #cache gets set, $form is written two times into the cache, once due to the storage and once due to #cache.

To summarize this simple patch
1. It fixes validation for multistep forms (prevent the rebuild!)
2. It fixes issues when populating form storage during form build

Furthermore thanks to the fix of point 2, it makes it possible to use the form storage in a clean way even when #cache gets activated. See post #1.

Imo the patch from #1 is an important patch which should get into d7 and d6! For d7 there is also a test (#4).

#11

Wim Leers - October 4, 2008 - 13:06

Subscribing.

#12

sun - October 4, 2008 - 15:21
Status:needs review» needs work

Can we add some inline comments explaining the how's and why's of the enhanced condition?

#13

fago - October 5, 2008 - 11:30
Status:needs work» needs review

ok, I've updated the patch.

Furthermore I've improved it a bit: When $form_state['rebuild'] is explicitly set, now it's too checked whether the form has been submitted and validation errors occured. It doesn't make sense to allow people skipping this check, so I've improved the patch to check it for both cases. As an affect you can set $form_state['rebuild'] in your form builder now too.

Updated and improved patch from #1 attached, applies to d6 & d7. The test for d7 (#4) still works fine once the patch is applied.

AttachmentSize
drupal_form_state_update_7.patch 1.9 KB
Testbed results
drupal_form_state_update_7.patchfailedFailed: Failed to install HEAD. Detailed results

#14

dropcube - October 5, 2008 - 13:40

The patch looks good for me. I tested and it prevents that forms that contains validation errors get rebuild during a multi-step process. The Form API docs should be updated to clearly explains the workflow.

#15

System Message - November 16, 2008 - 21:45
Status:needs review» needs work

The last submitted patch failed testing.

#16

lilou - November 17, 2008 - 13:29

#17

sun - December 4, 2008 - 02:04

Re-test.

AttachmentSize
drupal_form_state_update_7.patch 1.9 KB
Testbed results
drupal_form_state_update_7.patchfailedFailed: Failed to apply patch. Detailed results

#18

rb7 - December 8, 2008 - 21:08

Subscribing.

#19

bilgehan - December 26, 2008 - 18:56

Subscribing.

#20

Damien Tournoud - December 30, 2008 - 01:50
Status:needs review» needs work

Those two fixes looks reasonable at first sight. The test in #4 should be rolled in the patch (so that it can be tested by the test bot). There is some code style convention issues there (please see the Test writers guidelines for details). Plus I would like to be sure that the test cover both hunks (ie. it should break when any of them is not applied).

#21

doq - April 11, 2009 - 09:45

Subscribing.

#22

fago - April 15, 2009 - 14:43
Status:needs work» needs review

so, next try.

* I've updated the patch, as the form api received some improvements in the meanwhile.
* I took a look at the test writer guidelines (thanks damien) and overhauled the test. The test already covers both hunks.
* Attached is the updated patch for d7, containing the both, the changes and test.

AttachmentSize
form_storage_d7_v2.patch 7.19 KB
Testbed results
form_storage_d7_v2.patchpassedPassed: 10858 passes, 0 fails, 0 exceptions Detailed results

#23

Dries - April 17, 2009 - 20:37

Some feedback:

- We use dashes instead of underscores for menu/url paths. form_test/form_storage should be form-test/form-storage.

- The test case looks OK but took me some time figuring out. Some additional documentation would have helped. I wonder if the test could be simplified.

- In testFormCached() why don't we pass array('query' => 'cache=1') to both calls to drupalPost()? It seems like that could make for a better test.

Let's get this patch in!

#24

fago - April 21, 2009 - 20:49

thanks.

The exiting menu items of the already existing form-API test use the path "form_test" - so I stayed with form_test for consistency, but fixed it to use form_test/form-storage. So probably form_test should be fixed there to be form-test for all those menu items, but that's another issue..

>In testFormCached() why don't we pass array('query' => 'cache=1') to both calls to drupalPost()? It seems like that could make for a better test.

It doesn't matter the second time, as the form is already cached and won't be constructed again. But yes, it's more logical to add it to the second call too, so I've done so.

>Some additional documentation would have helped. I wonder if the test could be simplified.

Unfortunately I can't think of a way simplifying the test. Multistep forms with form storage aren't that simple ;) However I added some more comments to the form and the tests. I also improved the language to talk about "form constructions" not "form builds", as form builds could be easily confused with the build of form_builder(), which is happening for cached forms too and isn't counted.

AttachmentSize
form_storage_d7_3.patch 7.95 KB
Testbed results
form_storage_d7_3.patchpassedPassed: 10858 passes, 0 fails, 0 exceptions Detailed results

#25

fago - April 21, 2009 - 20:55

I forgot to rename the counting "builds" variable too, fixed that.

AttachmentSize
form_storage_d7_3.patch 7.98 KB
Testbed results
form_storage_d7_3.patchpassedPassed: 10858 passes, 0 fails, 0 exceptions Detailed results

#26

Dries - April 22, 2009 - 09:13
Status:needs review» fixed

Code looks good, fixes a real problem, comes with tests and documentation. Excellent work. Committed to CVS HEAD. Thanks fago.

#27

fago - April 23, 2009 - 13:55
Version:7.x-dev» 6.x-dev
Status:fixed» needs review

yeah, thanks!

So let's backport this to 6.x? The patch from #13 still applies fine to d6 and it's the same fix as got committed in d7.

#28

Alan D. - June 9, 2009 - 00:02

Thanks fago, the patch from #13 is working nicely on my local Drupal 6.12 install.

Is this still on the cards for a back port?

 
 

Drupal is a registered trademark of Dries Buytaert.