Drupal coder

Additional processing in Drupal's Webform 3 module

Webform is one of the most popular and most useful Drupal modules. It allows you to build forms using the administration interface without any programming. It saves the submitted data to the database and you can optionally let the data be sent to one or multiple e-mail addresses. And if that's not enough, you can even do some additional processing yourself like saving the results to an external system (using an API), logging extra data, ... This is accomplished using the "Additional processing (and validation)" fields available when you are editing your form.

At least... This used to be possible. With the imminent release of Webform 3 (at the moment of this writing beta 4 is the latest release), this won't be possible anymore (out of the box).

But you still need this functionality in your project? Lets see what your options are.

Use the Webform PHP module

While this functionality was removed from the Webform module itself, it was migrated to another contributed module named Webform PHP. So the easiest option to restore the "additional processing" functionality in Webform 3 is using this module.

This is in my eyes not the best option if you know something about module development (and I bet you do since you're going to write code for the extra processing). It's actually not the wisest thing to save code in your database.

Add an extra submit handler

What the Webform PHP module does, is just attaching an extra submit handler to the Webform. It then provides you with a text field where you can write your code. How can your write this submit handler in your own code?

/**
 * Implementation of hook_form_alter().
 */
function my_module_form_alter(&$form, &$form_state, $form_id) {
  // Affect all Webform forms.
  if (substr($form_id, 0, 20) == 'webform_client_form_') {
    // Add the submit handler after the existing Webform submit handler,
    // but before the second Webform handler. Pop off the first one and add
    // ours second.
    $first = array_shift($form['#submit']);
    array_unshift($form['#submit'], $first, 'my_module_client_submit');
  }
}

/**
 * FAPI #submit handler. Execute PHP submit handling.
 */
function my_module_client_submit($form, &$form_state) {
  $node = node_load($form_state['values']['details']['nid']);
  // process $form_state['values']
}

You can now do your additional processing in the extra submit handler my_module_client_submit. The submitted values are available in $form_state['values']['submitted_tree']. This array is indexed with the form key you specified for each component.

Implement hook_webform_submission_insert

One of the greatest thing in Webform 3 is that it introduces a lot of hooks. Webform used to be a pretty closed module. You couldn't (easily) define extra fields, work with Mollom, ... This is all history with the new release.

One of the hooks introduced is hook_webform_submission_insert. This hook lets you add some extra logic after the form submission is handled (data is saved, e-mail is sent, ...). When this hook is called, it is passed the current Webform node and the cleaned up submitted data (no $form_state structure but just the submitted data). When you look at the submitted data, there's only one issue with it: the keys of the array are the component ids instead of the form key you specified for each component.

stdClass Object
(
    [nid] => 5344
    [uid] => 1
    [submitted] => 1270708432
    [remote_addr] => 127.0.0.1
    [is_draft] => 
    [data] => Array
        (
            [1] => Array
                (
                    [value] => Array
                        (
                            [0] => Something I filled in here
                        )

                )
...

To convert the submitted data to a structure with the form keys as indexes, you can use this handy function.

function _my_module_webform_component_mapping($node) {
  $mapping = array();
  $components = $node->webform['components'];
  foreach ($components as $i => $component) {
    $key = $component['form_key'];
    $mapping[$key] = $i;
  }
  return $mapping;
}

This mapping gives you something like:

Array
(
    [name] => 1
    [firstname] => 2
)

So now you can get the name using $submission->data[$mapping['name']]['value'][0].

function my_module_webform_submission_insert($node, $submission) {
  if ($node->nid == MY_NODE_NID) {
    $mapping = _my_module_webform_component_mapping($node);
  
    $name = $submission->data[$mapping['name']]['value'][0];
  }
}
April 08, 2010Drupal, module development, webform

Comments

erewrwer

Very nice tutorial and helpful. Find the fleshed-out component names in the node was nice. I was about to go read them from the database, which isn't so fun.

You can also add a rule using the webform rules module. I would take this over the Webform PHP as it is a dead project on Drupal.org.

Thanks. Nice tutorial.

Thanks

Hi,
I have a field for zip code (US) in my webform. I need to make sure that only 5-digit numbers are entered in the field. I have the validation set up in my custom module but it's not working. The field still takes alphabets.

Here's the code:

function custom_module_form_alter(&$form, &$form_state, $form_id) {

if($form_id == 'webform_client_form_58') //get the id of the form, or webform
{
$form['#validate'][] = 'custom_module_form_validate';
return $form;
}
}

function custom_module_form_validate($form, &$form_state) {

$form_zipcode = $form_state['values']['submitted']['zip_code']; // get the value for zip code

if (!is_numeric($form_zipcode)) { // is the value entered a numeric one
form_set_error('', t('You must enter a 5-digit number for the zip code.'));
}
else if(preg_match("/^[0-9]{5}$/", $form_zipcode)) { // make sure it is a 5 digit number
form_set_error('', t('The ZIP code must be a 5-digit number.'));
}
}

Here is a short howto I wrote on getting custom code to run with Webform.
It details writing a custom module, copying the Webform template to match the nodeID, and adding custom javascript/jquery for form processing.

http://docs.quantact.com/drupal-webform-custom-php-code

Great article. How can I make the _my_module_webform_component_mapping work on hook_webform_submission_update? On update $node has less info than needed.

Hello I am trying to place a webform inside of a Drupal commerce checkout process. The checkout process has its own continue button which doesn't act to validate the information or save the information along with the checkout info. What would I have to do in order to get the continue button to check for validation and then submit and continue if the form is okay? thanks in advance. Not really a programmer but I have played with hooks. All suggestions are welcome.

Thanks for the great guide.
I think that $node is passed to $form in the submission handler, so you don't need to do a node_load

I found it useful to put a particular hidden field in each webform and set its default value to some value unique to each webform. Then, in my_module_client_submit, check $form_state['values']['submitted_tree']['your_hidden_field_name'] against your known hidden field values, and call different functions for each such value.

The reason I did this is that I didn't want to rely on the node's title (which could change), or the node's ID (which probably doesn't change, but I'm paranoid).

Thanks for this, bailed me out.

Thanks for this.

One comment though, i had fields with the same form_key.
This gives you problems when mapping.

Ok, I was working on webform 3.4 ! And after update on 3.6 it's now working.

Big thanks for the post :)

Hello,

Thanks for the post, really helpfull.
But I have one problem. I'd like to modify the file name when upload.
It's actually working, the correct name is set in the db.
But in the email confirmation, it is still the old file name.

Could you help me ?

Thx

Hello,

I was wondering where to write the extra submit handler.
Do I need to write a new module or just write that at the end of the webform.module or ....
That's not so clear to me.

Thanks!

Joost

Nice article.

I'd like to create new field types for webform:

* email+dns check: does a dns lookup on the email address' domain name, to see if it exists. This would give an error to this: john@email.foo

* spam honeypot (has css style `display:none;` and shouldn't be filled in. Catches spam bots that fill in all form fields)

* hidden field of page creation time. This hidden field is compared with page submit time, and if the submit time is less than 1 second after the page create time, then we assume that its a spam bot.

I want these new fields to be easy to install for novice Drupal users, perhaps each as its own module. Does webform allow modules of new input types?

Thanks for the info, I have used the Webform module on a project and it is very handy. Although I have one question/problem when it comes to advanced usage.

When you end up using hooks and the like, you are putting so much work into customization. Would the customization effort not be better spent on using CCK and allowing a hook to send the emails. This would then be a lot more compatible with other stored data in your Drupal system, which would then be easier to create other views and tools for.

I have only been using Drupal for about 4 months so please correct me if I am wrong.

What I would really like is a module that sends CCK entered in an email and gives you a confirmation screen. Maybe I will think about working one one??

Thanks for the heads up, Nate!

Thanks for writing this up! It's a great summary of exactly why the PHP fields were removed and alternatives that are now available.

I should note that your _my_module_webform_component_mapping() could have some problems though, since Webform allows multiple elements to have the same key as long as they're in different fieldset (which allows you to clone an entire fieldset and only change the key of the fieldset), so you might have some data clobering if you're in that situation.

What about integration with the Services module? Is this possible?

Post new comment

The content of this field is kept private and will not be shown publicly.
  • Web page addresses and e-mail addresses turn into links automatically.
  • Allowed HTML tags: <a> <em> <strong> <cite> <code> <ul> <ol> <li> <dl> <dt> <dd>
  • Lines and paragraphs break automatically.

More information about formatting options