Drupal 6: Programmatically Remove Required Field Property from Form

Suppose you are coding a form in Drupal 6 and you have a mandatory (required) field. However, this required field is only mandatory dependent upon the value in another field. This can happen quite often, and I encountered it at a client site the other day. We had a postcode field that was mandatory only if the country in a selection box was set to 'United Kingdom'. Any other country meant that the postcode field was not required.

So how do we code this? The solution required a little bit of consideration and there are two prongs of attack. Firstly, there is a need to change the validation rule once the form is submitted using the ['#after_build'] index in the $form array. Secondly, we need to do some client side work to hide or show the asterisk which denotes a required field dependent upon what the selection box is showing. This needs a couple lines of Javascript / jQuery.

The Code

remove_required_property.info

name = "Remove Required Property"
description = "Tutorial Code to Programmatically Remove the Required Property of a Form Field"
core = 6.x
php = 5.2
dependencies[] = "location"

Since we are using a drop down selection box with a list of countries we will need to use the location module API.
remove_required_property.module
<?php
/**
* Implementation of hook_menu().
*/
function remove_required_property_menu() {

   
$items['required-property'] = array(
       
'title' => 'Programmatically Remove Required Property From Form Field',
       
'page callback' => 'drupal_get_form',
       
'page arguments' => array('remove_required_property_form'),
       
'access callback' => TRUE,
       
'type' => MENU_NORMAL_ITEM,
    );

    return
$items;
}




function
remove_required_property_form() {

   
$form['your-details-postcode'] = array(
       
'#type' => 'textfield',
       
'#maxlength' => 20,
       
'#size' => 20,
       
'#title' => t('Postcode'),
       
'#required' => TRUE,
       
'#attributes' => array('class' => 'donation-your-details-field-text-input'),
    );

    if (
module_exists('location') and ($countries = location_get_iso3166_list(FALSE)))
       
$form['your-details-country'] = array(
           
'#type' => 'select',
           
'#title' => t('Country'),
           
'#required' => TRUE,
           
'#options' => $countries,
           
'#attributes' => array('onChange' => 'postcode_required()'),
           
'#default_value' => array('uk' => 'uk'),
        );   
    else
       
watchdog('remove_required_property', t('Unable to load list of countries'), NULL, WATCHDOG_ERROR);

   
$form['submit'] = array(
       
'#type' => 'submit',
       
'#value' => t('Submit'),
    );
       

   
$form['#after_build'][] = 'remove_required_property_form_after_build';
   
$form['#validate'][] = 'remove_required_property_form_validate';
  
    return
$form;
}
?>

There are a few noteworthy comments about this section of the code. I'm loading the countries using the location module location_get_iso3166_list call - obviously if your requirement doesn't need a selection box you can skip this! I have added an '#attributes' index into the $form array to load a call to the JavaScript function postcode_required() which is invoked on every change of the drop down selection box. Finally, there is the aforementioned '#after_build' reference which will check the submitted values and where we can manipulate the validation rules.
<?php
function remove_required_property_form_after_build($form, &$form_state) {

    if (isset(
$form['#post']['your-details-country'])
        and
$form['#post']['your-details-country'] != ''
       
and $form['#post']['your-details-country'] != 'uk')
       
$form['your-details-postcode']['#required'] = NULL;

    return
$form;
}


function
remove_required_property_form_validate($form, &$form_state) {

   
// postcode. Required if the country is UK
   
if ($form_state['values']['your-details-postcode'] == '' and $form_state['values']['your-details-country'] == 'uk')
       
form_set_error('your-details-postcode', t('You must specify a postcode for addresses within the United Kingdom'));
}



/**
* Implementation of hook_form_alter().
*/
function remove_required_property_form_alter(&$form, $form_state, $form_id) {

    if (
$form_id == 'remove_required_property_form') {
       
$mod_path = drupal_get_path('module', 'remove_required_property');
       
drupal_add_js("{$mod_path}/js/rrp.js");
       
drupal_add_css("{$mod_path}/css/rrp.css");
    }
}
?>

The function remove_required_property_form_alter() is the opportunity to check what has been submitted and if the country is not uk then I don't need the '#required' property so it can be set to NULL. The remove_required_property_form_validate() function is where I do the validation of the postcode - note I'm only checking whether the field is empty or not here. In a live production system something more robust would be required. I use hook_form_alter as an opportunity to load the JavaScript and the CSS.
<?php
/**
* Implementation of hook_theme().
*/
function remove_required_property_theme() {

    return array(
'remove_required_property_form' => array(
           
'template' => 'includes/remove_required_property_form',
           
'arguments' => array('form' => NULL)));
}
?>

Finally we need a call to hook_theme to set the form to an external template. This will be needed since we are going to have to munge the Drupal generated HTML code.

includes/remove_required_property_form.tpl.php

<table>
<tbody>
    <tr class="rrp-table-padding">
        <td class="rrp-field-text rrp-your-details-leftcell">
            <?php
               
// Postcode
               
$output = drupal_render($form['your-details-postcode']);
                if (
preg_match("#(.*)(<input.*)#mis", $output, $match))
                    print
str_replace("<span ", "<span id=\"rrp-postcode-required\" ", $match[1]);
               
error_log($match[1]);
           
?>

        </td>

        <td>
            <div class="rrp-textfield">
                <?php if (isset($match[2])) print $match[2]; ?>
            </div>
        </td>
    </tr>

    <tr class="rrp-table-padding">
        <td class="rrp-field-text rrp-your-details-leftcell">
            <?php
               
// Country
               
$output = drupal_render($form['your-details-country']);
                if (
preg_match("#(.*)(<select.*)#mis", $output, $match))
                    print
$match[1]; 
           
?>

        </td>

        <td>
            <div class="rrp-select">
                <?php if (isset($match[2])) print $match[2]; ?>
            </div>
        </td>
    </tr>

</tbody>
</table>
<?php print drupal_render($form); ?>

The form's template file presents an opportunity to tabulate the data and make it a little more user friendly. The first requirement is to insert a css tag for the 'required' asterisk. The problem with Drupal 'required' asterisks is they do not have id tags to aid uniqueness so we need to add one with the id of rrp-postcode-required using the PHP str_replace function. Also note I'm calling drupal_render on individual form elements - this marks each one as printed, and then finally I issue another call to drupal_render which will print any form elements not already output - in this case the submit button which falls outside of the HTML table.

js/rrp.js

function postcode_required() {

    if (($('#edit-your-details-country').val() == 'uk')) {
        $('#rrp-postcode-required').show();
    } else {
        $('#rrp-postcode-required').hide();
    }
   
}

The very simple JavaScript is called whenever the drop down selection box's value is changed. It will look at the current value, and if it is 'uk' then ensure that the asterisk is displayed, else ensure it is hidden.

css/rrp.css

#rrp-postcode-required {
    visibility: visible;   
}

Some even simpler CSS. The rrp-postcode-required tag is set to initially visible when the form is loaded.

Screen Images




Remove Required Property

Remove Required Property - UK Selected, Asterisk on Postcode
The second image shows that the asterisk is now hidden because the country has been changed.



Remove Required Property

Remove Required Property - UK Not Selected, No Asterisk on Postcode


AttachmentSize
rrp.tar.gz1.76 KB