1

I have to call a webservice than dynamically modify the form. I used hook_form_FORM_ID_alter to do that. The problem is hook_form_FORM_ID_alter is also called on submit event when the form is not going to be displayed again to the user.

How can I avoid calling the webservices two times? The information gathered with the webservice are not important for the validation...

This is also linked to this question hook_form_FORM_ID_alter called twice after submit

6
  • No the form is not displayed after submit. I'm pretty sure it is called. At least in my tests. I think it's used to compare the submitted form with the "original" one so user can't submit value of field than doesn't exist in the original form.
    – gagarine
    Commented Jul 29, 2014 at 21:17
  • so why aren't you using an #ajax element on your $form to call the webservice and change it whenever the user sees the form?
    – tenken
    Commented Jul 29, 2014 at 21:48
  • So you only want it to call on the initial form render, not on submission regardless of whether validation fails? Or do you also want it to run if the form is re-rendered after a validation error?
    – rooby
    Commented Jul 30, 2014 at 0:37
  • @tenken #ajax is not an option, it have to works without JS.
    – gagarine
    Commented Jul 31, 2014 at 12:47
  • @rooby I need a call if the form fail on validation but not if the form is submitted and the validation pass.
    – gagarine
    Commented Jul 31, 2014 at 12:49

6 Answers 6

5
+50

The reason why your alter is called twice is because Drupal needs to build the form at least two times: one for the first time is delivered to the user and a second time when the submit is done. The second build is needed because Drupal needs to know the complete form that was delivered to the user (for validation and security reasons).

You have two approaches to solve this:

Form API

As other have answered modify the form API behavior to avoid that second call:

  • Detect the second call and do not call the webservice (checking $form_state['triggered_element']['#name'] as told): it's ok but take into account that if the form built in the second call is different form the first one you may have problems with Form API complaining about invalid user input,

  • Enable form cache ($form_state['cache'] = TRUE). This way Drupal saves the form when is built for the first time, so the second time during submit, form is fetched form DB.

Cache webservice results

A second call to the webservice is not a problem if you cache webservice data because you retrieve the results from the cache instead of call it again. I think this is the best option if the nature of the webservice data allows to cache it.

This way the problem is solved in the webservice area while the other solutions involves that other components solve the issue (your form API code solves the webservice problem). If there's another similar use case you will have to solve again the proble, but if you solve it using webservice caching the problem is solved for all use cases.

1

You could check it the $form_state['triggered_element']['#name'] value matches your submit button #name.

Example:

function mycustom_form($form, &$form_state) {
  ...

  if (!empty($form_state['triggering_element']['#name'])
    && $form_state['triggering_element']['#name'] == 'submitname') {
    // DO STUFF
  }

  $form['submit'] = array(
    'type' => 'submit',
    'name' => 'submitname',
    'value' => 'Save',
  );

}

You can also check other triggering_element properties as #value but #name is more accurate.

Note: ajax callbacks are also defined on the triggering_element form property this is the reason you should check the #name.

1

The reason you're seeing this behaviour is that the FAPI needs the form array for two different page requests: the form view, and the form submit. By default it recreates the form on submission for this, but you can tell it to cache the form instead and just reload it:

$form_state['cache'] = TRUE;

This will still mean that every time the user loads the initial form a new web service request will be made, so you might want to implement caching for the service itself. (Especially if this form is open to the public, you probably don't want to have a direct link between a page view and a web service request, but I don't know the specifics of your situation.)

0

If you set a value in your form with the form alter, either the result of the webservice or an arbitrary value you can then test if this value is set and if it is not set call the webservice.

0

I'd recommend exploring the $form_state array in your alter hook. You'll notice a couple of elements that you can key off of. I believe you'll want to look at $form_state['submitted']. This should be FALSE when the form is first loaded and TRUE when the form is submitting.

So your webservice call can be made when $form_state['submitted'] == FALSE.

The drupal_build_form gives a good description of the various $form_state elements.

10
  • I vouch for this answer. The form state variable is the key. Also, when the form is created, the form state values for the form elements are relatively empty (empty arrays, strings, etc.), unlike when the form is submitted, where form state is populated as well. But I think form state submitted is probably a better way to go. Commented Jul 30, 2014 at 16:23
  • 1
    $form_state['submitted'] won't be TRUE in a hook_form_alter() just because it's being built for submission. It will be TRUE if the form is being rebuilt (eg. failed validation, multistep form, etc.).
    – Andy
    Commented Jul 30, 2014 at 16:39
  • @Andy when the form is submitting (as in the user clicks submit) the $form_state['submitted'] will be TRUE. The drupal_build_form gives a good description of some of the form_state elements. api.drupal.org/api/drupal/includes%21form.inc/function/…
    – Craig
    Commented Jul 31, 2014 at 13:16
  • That description works for a validation or submission handler, or if the form is rebuilt. It will not be set for a "normal" form being built for submission. Setting $form_state['submitted'] is part of processing the form (drupal_process_form() calls form_builder()) whereas the alters are called in drupal_prepare_form() which is earlier. It's easy to verify (and I just have!).
    – Andy
    Commented Jul 31, 2014 at 13:47
  • @Andy... I'm confused. When I use hook_form_alter, I see that $form_state['submitted'] is FALSE. I can programmatically check for when it is TRUE, which for me has just been submitting the form. Am I misunderstanding what you're saying?
    – Craig
    Commented Jul 31, 2014 at 16:39
0

For your particular situation, I would recommend you cache the results of the webservice call in $form_state and set $form_state['cache'] = TRUE. As others have said, this will cause Drupal to retrieve the form (and form state) from the database (or other cache system); otherwise the form will need to be built again before the form processing recognizes the form is being submitted. So your alter hook should look like this:

function hook_form_FORM_ID_alter(&$form, &$form_state) {
    $form_state['cache'] = TRUE;
    if (!isset($form_state['storage']['mine'])) {
        // Call the web service and store the data in $form_state['storage']['mine'].
    }
    // Now use $form_state['storage']['mine'] to alter the form.
}

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.