Problem/Motivation

If the same form is on a page multiple times and with different arguments, submitting the second form may accidentally be processed with the first form's arguments. Steps to reproduce:

- Have a simple form that does not use #ajax. The #ajax property will force $form_state['cache'] = TRUE, and inadvertently "fix" the bug.
- Pass in different parameters to the form and call it twice on the same menu path.
- Submit the second form.
- You get the results from the first form's arguments, rather than the second one.

Proposed resolution

Create a consistent hash or indicator that can be used across page requests to identify when the same form has been built multiple times on the same page. Actual solution TBD. But some possibilities include:

  • This may be a "form_build_count" based on the order the form was built.
  • An automatic "form_args_hash" that is made up of the original arguments to separate the two identical forms.
  • A manually-provided indicator to separate if the form needs to be supported on the same page multiple times.

Remaining tasks

Solution to be determined. Test coverage expanded to include this bug.

User interface changes

None.

API changes

TBD

Data model changes

None.

Comments

quicksketch created an issue. See original summary.

quicksketch’s picture

Note that this issue was spun off from #2819375: Do not make entries in "cache_form" when viewing forms that use #ajax['callback'] (Drupal 7 port), which is a Drupal 7 specific problem that has already been solved in Drupal 8. As for this particular issue, I am not sure if Drupal 8 has this same problem or not.

ckaotik’s picture

Version: 7.x-dev » 8.3.x-dev

If just ran into this on a Drupal 8 installation. Was displaying a form in a block multiple times with different arguments (pulled from the block config). Drupal would always submit the first occurence of the form.

As mentioned in the issue description, enabling caching of the form state can be used as a workaround, and once I added this I could successfully submit either form.

$form_state->setRequestMethod('POST');
$form_state->setCached(TRUE);

Version: 8.3.x-dev » 8.4.x-dev

Drupal 8.3.6 was released on August 2, 2017 and is the final full bugfix release for the Drupal 8.3.x series. Drupal 8.3.x will not receive any further development aside from critical and security fixes. Sites should prepare to update to 8.4.0 on October 4, 2017. (Drupal 8.4.0-alpha1 is available for testing.)

Bug reports should be targeted against the 8.4.x-dev branch from now on, and new development or disruptive changes should be targeted against the 8.5.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Version: 8.4.x-dev » 8.5.x-dev

Drupal 8.4.4 was released on January 3, 2018 and is the final full bugfix release for the Drupal 8.4.x series. Drupal 8.4.x will not receive any further development aside from critical and security fixes. Sites should prepare to update to 8.5.0 on March 7, 2018. (Drupal 8.5.0-alpha1 is available for testing.)

Bug reports should be targeted against the 8.5.x-dev branch from now on, and new development or disruptive changes should be targeted against the 8.6.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

hugronaphor’s picture

I have a similar problem but not identical.
I do use #ajax and only one form is loaded on the page, however when I set() a randomly generated value on form build - on submit callback I get a completely different one.

@ckaotik snipped solved it.

Version: 8.5.x-dev » 8.6.x-dev

Drupal 8.5.6 was released on August 1, 2018 and is the final bugfix release for the Drupal 8.5.x series. Drupal 8.5.x will not receive any further development aside from security fixes. Sites should prepare to update to 8.6.0 on September 5, 2018. (Drupal 8.6.0-rc1 is available for testing.)

Bug reports should be targeted against the 8.6.x-dev branch from now on, and new development or disruptive changes should be targeted against the 8.7.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

wells’s picture

Similar situation here -- custom form with a configuration option to submit via AJAX or redirect to a specific node after submit. If two instances of the form are loaded on the same page, only the initial form's configuration is used.

E.g. if the first instance is configured with a redirect and the second is configured to use AJAX, the second will throw an error on submit because the submit handler attempts to use the configuration from the first form. In this case, the error thrown is:

Symfony\Component\HttpKernel\Exception\HttpException: The specified #ajax callback is empty or not callable. in Drupal\Core\Form\FormAjaxResponseBuilder->buildResponse() (line 67 of core/lib/Drupal/Core/Form/FormAjaxResponseBuilder.php).

@ckaotik's workaround in #3 resolved this situation as well, when added to the custom form's buildForm method.

khiminrm’s picture

#3 helped me to fix similar problem with 3 same ajax forms with different arguments.

@ckaotik, thanks!

drupix’s picture

Same form multiple time on a page, no ajax, same problem... Always the first occurrence of the form was submitted.
#3 fixed my Sunday headache ;-)

Thanks @ckaotik !

izus’s picture

Same issue experienced here with no ajax at all : same form multiple times in a controller

just added the lines from #3 to my buildForm() method and everything worked fine

Thanks @ckaotik

oknate’s picture

I ran into a related issue with entity browser: #3036406: Edit button of same type doesn't work..

Entity form of the same type is loaded via ajax link, and the ajax POST data infects the form loaded by ajax, so the form for node 2 loads with post data from form 1.

antonnavi’s picture

I experienced this issue too. To fix this issue I used alternative way: I added "diversity" to my form ID with code:

  public function getFormId() {
    static $count = 0;
    $count++;

    return 'my_custom_form_id_' . $count;
  }

Can be used as alternative (to solution from #3) way.

cilefen’s picture

lamp5’s picture

Thx @ckaotik, you saved my day!

makazimtingwa’s picture

@Antonnavi Peace & blessings to you.... this saved me at 2:00am. For some reason #3 isn't working for me in 8.7.8.

codex42’s picture

#3 works! Thanks. I used it in buildForm()

Version: 8.6.x-dev » 8.8.x-dev

Drupal 8.6.x will not receive any further development aside from security fixes. Bug reports should be targeted against the 8.8.x-dev branch from now on, and new development or disruptive changes should be targeted against the 8.9.x-dev branch. For more information see the Drupal 8 and 9 minor version schedule and the Allowed changes during the Drupal 8 and 9 release cycles.

introfini’s picture

I had this problem with an ajax callback, #3 solved it for me. Thanks!

fabiansierra5191’s picture

The workaround #3 works, I'm wondering if this is the proper way to fix this? Could this generate cache issues?

Version: 8.8.x-dev » 8.9.x-dev

Drupal 8.8.7 was released on June 3, 2020 and is the final full bugfix release for the Drupal 8.8.x series. Drupal 8.8.x will not receive any further development aside from security fixes. Sites should prepare to update to Drupal 8.9.0 or Drupal 9.0.0 for ongoing support.

Bug reports should be targeted against the 8.9.x-dev branch from now on, and new development or disruptive changes should be targeted against the 9.1.x-dev branch. For more information see the Drupal 8 and 9 minor version schedule and the Allowed changes during the Drupal 8 and 9 release cycles.

geek-merlin’s picture

Issue tags: +Needs tests

This is a quite complex issue, and FormBuilder a quite convoluted piece of code now.
So a test that reproduces the issue may help someone(tm) to debug this further.
Note that POST and GET method forms may need different approaches, so having both might help.

jeroent’s picture

Status: Active » Needs review
Issue tags: -Needs tests
StatusFileSize
new4.65 KB

I wrote a test that illustrates the problem.

Status: Needs review » Needs work

The last submitted patch, 23: 2821852-23.patch, failed testing. View results

jonas139’s picture

I was having the same issue and #3 did the trick for me.
It's a simple form without any ajax.

mxr576’s picture

Version: 8.9.x-dev » 9.3.x-dev
mxr576’s picture

Issue tags: +Needs reroll
ayushmishra206’s picture

Assigned: Unassigned » ayushmishra206
ayushmishra206’s picture

Assigned: ayushmishra206 » Unassigned
Status: Needs work » Needs review
StatusFileSize
new3.12 KB

Rerolled for 9.3.x , Please review.

Status: Needs review » Needs work

The last submitted patch, 29: 2821852-29.patch, failed testing. View results

spokje’s picture

Issue tags: -Needs reroll
StatusFileSize
new3.12 KB

Tag clean-up, also re-uploaded the patch from #29 to make it more clear it's a failing-test-only patch, not a solution.

Currently known workarounds: #3 and #13.

sharique’s picture

Both the fixed doesn't help in case of webform, it is being tracked in https://www.drupal.org/project/webform/issues/3215639.

spokje’s picture

Issue tags: +Bug Smash Initiative
grimreaper’s picture

Hello,

Thanks for the 2 lines comment 3, saved my day!

Version: 9.3.x-dev » 9.4.x-dev

Drupal 9.3.0-rc1 was released on November 26, 2021, which means new developments and disruptive changes should now be targeted for the 9.4.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

Version: 9.4.x-dev » 9.5.x-dev

Drupal 9.4.0-alpha1 was released on May 6, 2022, which means new developments and disruptive changes should now be targeted for the 9.5.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

Version: 9.5.x-dev » 10.1.x-dev

Drupal 9.5.0-beta2 and Drupal 10.0.0-beta2 were released on September 29, 2022, which means new developments and disruptive changes should now be targeted for the 10.1.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

lubwn’s picture

#3 fixed this issue for me as well. I almost pulled my hair off on this since I did not understand why it sends different form than it should, even when form_id was different. Thanks a lot!

Version: 10.1.x-dev » 11.x-dev

Drupal core is moving towards using a “main” branch. As an interim step, a new 11.x branch has been opened, as Drupal.org infrastructure cannot currently fully support a branch named main. New developments and disruptive changes should now be targeted for the 11.x branch, which currently accepts only minor-version allowed changes. For more information, see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

ammaletu’s picture

The two lines in #3 indeed fix this issue -- but they also potentially open up a new, horrible bug! We added these lines, and now when more than one guest user opens a page with this form, only one of them can submit the form (whoever is quickest). The rest get a "Integrity constraint violation, duplicate entry for node_field__uuid__value" error. This only happens for guest users, because for them the whole page is cached. As I said, a horrible bug for end-users and hard to spot when testing alone.

Think very hard before simply adding these two lines. Are these forms which are displayed to guest users and do you have the static page cache enabled? Not caching the form and using a unique form ID might be the better solution. We will for now try to simply not cache pages which have these forms. Together with the cache kill switch, the fix from #3 seems to work.

sukr_s’s picture

The issue it seems that the block loads the configuration for the first block found and therefore the arguments of the first form are used. However if you check the user input in $form_state, the values sent from the browser are correct. I'm assuming that the arguments are stored in hidden fields. If so, you can check for user input for these values and use them if found, otherwise use from arguments
e.g.

public function buildForm(array $form, FormStateInterface $form_state, $id) {
    // Get user input values.
    $input_values = $form_state->getUserInput();
    $form['id_value'] = [
      '#type' => 'hidden',
      // Check if the key is in user input values. If it's not found, then use from argument.
      '#value' => isset($input_values['id_value'])? $input_values['id_value'] : $id,
    ];
    $form['txt'] = [
      '#type' => 'textfield',
      '#title' => 'Textfield',
      '#default_value' => $id,
      '#required' => TRUE,
    ];
    $form['submit'] = [
      '#type' => 'submit',
      '#value' => 'Submit'
    ];    
    return $form;
}

with this approach the submit and validate have the correct values for the argument fields.

johnpitcairn’s picture

#3 has saved my day after an awful lot of thrashing, thanks.

I'm not seeing the bug in #40, but we do not have page_cache enabled. It seems fine with the dynamic page cache.

To consistently target multiple instances of the same form on the page with ajax and #states, I'm passing a unique id into the form build from my block plugin, so no form processing will modify it when the form is rebuilt. I've found the following is needed when generating the id to avoid appending a hash on ajax requests.

Html::setIsAjax(FALSE);
$id = Html::getUniqueId('my-form');
$form = $this->formBuilder->getForm($form_class, $id);

Version: 11.x-dev » main

Drupal core is now using the main branch as the primary development branch. New developments and disruptive changes should now be targeted to the main branch.

Read more in the announcement.

rob230’s picture

The lines in #3 are not fixing it for me.