Adding custom validation

Our Fictitious Conference organizers have stipulated that a maximum of one presentation proposal is allowed per potential speaker registering via our Speaker Registration form.

The organizers have requested us to put validation in place that will deny a submission where a particular First name, Last name, and Email address combination has previously been submitted, unless the subsequent submission is being made by one of the administrators on behalf of a potential speaker.

Their thinking is that the inclusion of Email address in the check will prevent the possibility of two people with the same First name and Last name being denied a submission because the other person has already made a submission. Another part of the need for this change in validation strategy is that there are still folks in this world who share e-mail addresses (don't laugh, it happens).

When we added the Email address component to our Speaker Registration form way back in Chapter 2, Trying Out Webform, we enabled validation to ensure that data input to this field would be unique, that is no two submissions would be permitted to have the same value in this field. In view of the complaints from those who share e-mail addresses, this uniqueness test will need to be disabled and replaced by the compound validation rule described previously.

Neither Webform nor Webform Validation, natively allow for this kind of compound field input validation, so we're going to create a custom module to implement the business rule by extending the Webform Validation module.

Getting ready

Before we get carried away by the excitement of a new plugin module, let's navigate to our Speaker Registration form, click on the Webform tab, and then on Edit next to our Email address component. Under the VALIDATION fieldset we need to uncheck the box at Unique and click on Save component to make the change effective. Now that we have removed Webform's built-in uniqueness validation on the Email address component, we can replace it with our own.

As before, we are rapidly going to charge through the preliminaries of getting our new webform_validation_custom module in place.

  1. Create a custom module folder called webform_validation_custom in sites/all/modules so that we have the folder path sites/all/modules/webform_validation_custom.
  2. Next add our module info file called webform_validation_custom.info into the sites/all/modules/webform_validation_custom folder we just created. Edit the file to contain the following text and save the changes:
    name = Webform Validation Custom
    description = Custom validation rules for Webforms.
    core = 7.x
    package = Webform
    dependencies[] = webform
    dependencies[] = webform_validation
    
  3. We add our module file by creating an empty file called webform_validation_custom.module in our module folder. We add the following lines of code to our module file and save the changes:
    <?php
    /**
    * @file
    * Webform Validation Custom.
    */
    
  4. Finally, we navigate to our Drupal Modules administration page, check the box to enable our bare-bones module and click on Save configuration.
Getting ready

Okay, let's get on with adding our custom validation rule coding using the hooks exposed by the Webform Validation contributed module. We may find benefit in having a look at the helpful README.txt file in the sites/all/modules/webform_validation folder as we work through this exercise.

How to do it...

We are now ready to extend the Webform Validation module by adding the code required to test each submission and ensure that a previous First name, Last name, and Email address combination has not been submitted for a second time. We will be creating a brand new Webform Validation option to implement this code so that similar functionality is available to all Webforms across our site.

There are three functions required for us to achieve this. The first function implements a hook describing our new validator to the Webform Validation administrative pages.

The second function is the hook that will apply the validation processing when it is required by a form submission, that is, our form has an active validation rule defined for the new validation option. This hook will call our third function which does the detailed work of the validation itself.

  1. Implementing the hook_webform_validation_validators() function is where we detail the new validator option to Webform Validation in our webform_validation_custom.module file:
    /**
    * Implements hook_webform_validation_validators().
    */
    function webform_validation_custom_webform_validation_validators() {
    return array(
    'uniqueconcatenated' => array(
    'name' => 'Unique concatenated values',
    'component_types' => array(
    'textfield',
    'email',
    'hidden',
    ),
    'custom_error' => TRUE,
    'min_components' => 2,
    'description' => t('Verifies that a concatenation of selected user-entered values is unique'),
    ),
    );
    }
    
  2. Deciding when to actually perform the required validation is handled by the hook_webform_validation_validate() function which we also code into our webform_validation_custom.module file:
    /**
    * Implements hook_webform_validation_validate().
    */
    function webform_validation_custom_webform_validation_validate($validator_name, $items, $components, $rule) {
    global $user;
    if ($items && $user->uid == 0) {
    $errors = array();
    switch ($validator_name) {
    case 'uniqueconcatenated':
    $errors = webform_validation_custom_uniqueconcatenated($items, $rule);
    return $errors;
    break;
    }
    }
    }
    
  3. The burden of processing the validation requirement lies with the webform_validation_custom_uniqueconcatenated() function code that we add in next:
    /**
    * Search previous submissions for occurrences of what should be a
    * unique concatenation of two or more user input field values.
    *
    * @param array $items
    * The items (component values) applicable to this validation rule.
    * @param array $rule
    * An array of data defining the validation rule.
    * @return array
    * An error if duplicates were found, or empty if no duplicates.
    */
    function webform_validation_custom_uniqueconcatenated($items, $rule) {
    $errors = array();
    // Build concatenated search criteria and get component id's.
    $search = '';
    $cids = array();
    foreach ($rule['components'] as $key => $component) {
    $index = 'item_' . $component['cid'];
    $search = $search . $items[$index];
    $cids[] = $component['cid'];
    }
    $search = strtolower($search);
    //Retrieve all submitted data for required components from database.
    $nid = $rule['nid'];
    $sid = $rule['sid'];
    $submissions = db_select('webform_submitted_data', 'wsd')
    ->fields('wsd', array('sid', 'cid', 'data'))
    ->condition('nid', $nid)
    ->condition('cid', $cids, 'IN')
    ->condition('no', 0)
    ->orderBy('sid')
    ->orderBy('cid')
    ->execute()
    ->fetchAll();
    // Build array of comparison strings from all submissions.
    $previous = array();
    foreach ($submissions as $submitted) {
    $index = $submitted->sid;
    $value = strtolower($submitted->data);
    if (isset($previous[$index])) {
    $previous[$index] = $previous[$index] . $value;
    }
    else {
    $previous[$index] = $value;
    }
    }
    // Error if one or more submissions match our search criteria.
    if ($previous) {
    $matches = array_keys($previous, $search);
    if (count($matches) > 0) {
    $index = 'item_' . $cids[0];
    $errors[$index] = _webform_validation_i18n_error_ message($rule);
    }
    }
    return $errors;
    }
    

We can now save the changes to our webform_validation_custom.module file and return to our Speaker Registration form to see our extension module in action.

How it works...

In our first bit of code, the webform_validation_custom_webform_validation_validators() hook, we defined a new validator to the Webform Validation module. With the information we supplied in the array, Webform Validation builds the administrative functionality for the new validator option. Let us make our way to the administrative page by clicking on the Webform tab of our Speaker Registration form and then on Form validation.

How it works...

We see that the validation rule we set up in the Advanced data validation section of Chapter 6, Extending Webform, is still defined. Scrolling down to the bottom of this page we find our new validator option has been added to the list of validation rules we may add.

How it works...

Comparing the screen display to the array we defined in our validator's hook we notice that three of the array elements have been utilized here.

The clickable heading comes from the name element and the description element provides basic information regarding the rule type. Appended to the description we find the component types listed in our component_types array (that is, textfield, email, and hidden), with the addition of select. Since we enabled the Select or other... component type module (which is capable of accepting text input in its other textfield) in Chapter 6, Extending Webform, Webform Validation automatically adds the select component type to any rule which permits the textfield component type.

Let us click on the Unique concatenated values heading to learn how we may interact with our validator by adding a validation rule.

How it works...

Our first step is to supply a name for our rule, so let us enter Unique name, surname, email combination as the Rule name.

Listed below the Rule name textfield are all of the components of our Speaker Registration form which are of the specified allowable component types, in form weight sequence. Let's check the boxes next to the First name, Last name, and Email address options to include these in our validation processing.

The final step is to supply the error message that will appear on our Speaker Registration form when an existing First name, Last name, Email address combination is repeated in a submission attempt. The Custom error message field has been placed here as a mandatory field by the Webform Validation module because we set the custom_error element of our validator definition array to true. Let us enter Only one registration is permitted per speaker in the Custom error message textfield and click on Add rule to save our changes.

How it works...

There is one last aspect of our validator definition array that we have not yet seen in action. Let us click on Edit next to our Unique name, surname, email combination rule name, uncheck the boxes at Last name and Email address and submit our changes by clicking on the Edit rule button.

How it works...

We defined our validator as requiring a minimum of two components in the min_components element, because the very principle of concatenation implies at least two values being strung together. The Webform Validation administrative system will not permit us to save this rule with only the one selected component, so let us recheck the boxes at Last name and Email address and click on Edit rule to restore our rule to what it was.

Having covered everything as far as our first hook is concerned, let's turn our attention now to the second hook, our webform_validation_custom_webform_validation_validate() function and touch on the Drupal Forms API magic that helps the process along.

First, a bit of background information to contextualize the discussion somewhat. Once activated, the Webform Validation module adds a validation handler into the form array of every Webform by means of a hook_form_alter() function (see webform_validation_form_alter() in webform_validation.module). When these Webforms are submitted, Drupal form processing will pass control to Webform Validation during the validation phase of processing the submitted data.

Webform Validation goes through all of the rules defined for the specific Webform node, builds up arrays of data regarding the rule, the components it pertains to and the values submitted for those components. This data is passed to the webform_validation_validate() function (also to any additional hook implementations, such as ours) so that required validation processing may occur.

Should validation errors be found, Webform Validation will set form errors, causing the submission to fail when control passes back to Drupal.

Our webform_validation_custom_webform_validation_validate() hook is quite straightforward. If there are $items to process (meaning that the current Webform page contains components that require validation and data was submitted for these components) and a validation rule has been defined for our uniqueconcatenated validator, the logic calls our webform_validation_custom_uniqueconcatenated() function to perform the validation processing and determines whether or not we need to raise an error.

The validation logic in the webform_validation_custom_uniqueconcatenated() function operates in four distinct steps.

Firstly we build our concatenated search string by stringing together the values submitted for the components. The concatenated string is then converted to lowercase in an effort to prevent attempted validation bypasses such as users changing the name Dan to dAn, for example.

Next we query the database table where Webform stores all submitted data and extract all previously submitted values for the relevant components. The returned data is also converted to lowercase, then concatenated together in an array called $previous.

Finally, we search the $previous array for other occurrences of our search string and count the number of matches found, flagging an error if one or more matches exist.

Let's test our validation by returning to the Speaker Registration form and attempt to complete two submissions with the same First name, Last name, and Email address, for example Dan, Smith, and respectively.

How it works...

Our second attempt to register as Dan Smith is thwarted by our custom extension to Webform Validation. Of course, our validation can yet be bypassed by suitably imaginative users. For example, if we were to change our test First name to Danny in this instance, the submission would be accepted.

When it comes to validation of submitted data we cannot hope to win every battle, so long as our efforts are well directed at winning the war. The important thing is to cover as many bases as possible without rendering our forms almost completely unusable with overly stringent validation requirements.

There's more...

We have covered the basics of the Webform Validation and how it interacts with Webform. Webform Validation has a few more tricks up its sleeve in terms of what is possible via the hooks it makes available. However, describing the other options would require more space than we have available here. Possibly the best way to learn is to follow the suggestion in the modules README.txt file to make a study of the built-in validators and their validation methods.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset