Theming forms with template.php by overriding theme functions

Submitted by Varun on

Here we are confronted with a situation where the default markup of a form on your site needs to have a different look. For instance when we need the form to be located in a block(eg. in a sidebar) rather than in the main content region of a page. Theming overrides are necessary here to make the form input fields shorter in order to fit within the sidebar region. This is accomplished by editing the template.php file and then creating a corresponding xxx.tpl.php file for the theme.

Theme override screenshot

Another option here would be to make changes to how the form is rendered by creating a custom module to do the job. Since I do not wish to change any of the actual underlying form functionality(eg. validation or adding submission actions) we will stick with changes at the theme level.

N.B. This means that if, for instance, I am removing an HTML form element, the element is not actually removed from the form processing meaning that theoretically a person could still input this form element because the form processing code will still be looking for this element in the $_POST array. In most cases, this does not present any real risk.

Figure out name for the function

So, my first step was to find out what my override function will be called. By looking on the source of the page where the form shows up, I found the unique form ID.

...
<input type="hidden" name="form_id" id="edit-connect-form-block" value="connect_form_block"  />
...

In addition to noting the names given to all of the input values I wish to work with. the source also told me that the function in template.php would be called "phptemplate_connect_form_block". This means that I my code looked like this in template.php:

<?php
function phptemplate_connect_form_block($form) {
  global 
$user;
//theme overriding code here 

//assign form input to variables 
    

$vars = array('user' => $user'form' => $form);

     
      

//(we can get to form input by looking at $form with dsm() or print_r() )

      

$vars['fname'] = drupal_render($form['connect_action_content_replace']['field_person_name_first']);
      
$vars['lname'] = drupal_render($form['connect_action_content_replace']['field_person_name_last']);
      
$vars['email'] = drupal_render($form['connect_action_content_replace']['field_address_email']);

//callback
return _phptemplate_callback('connect_edit_block'$vars);
}
?>

The xxx.tpl.php file

The callback here means that I can now add whatever markup I like to this form because the template will now look for a file called connect_edit_block.tpl.php which I created to look like this:

<div class="fname"><?php print $fname; ?></div>
<div class="lname"><?php print $lname; ?></div>
<div class="email"><?php print $email; ?></div>

When in doubt, print_r()

Now to return to my original issue and actually do something useful with the theme override function; I want to shorten the length of my input fields. In order to do this I need to alter the #size attribute of the particular form elements. By using a print_r() to view all the elements' attributes/properties I am able to determine the path to the attribute I wish to change. Note that if the devel module is enabled, using the dsm() function instead will print a much prettier form array.

Below you can see where I've added code to view the form array:

<?php
function phptemplate_connect_form_block($form) {
  global 
$user;

return 

print_r($form); //or dsm($form);
/*
    $vars = array('user' => $user, 'form' => $form);

      $vars['fname'] = drupal_render($form['connect_action_content_replace']['field_person_name_first']);
      $vars['lname'] = drupal_render($form['connect_action_content_replace']['field_person_name_last']);
      $vars['email'] = drupal_render($form['connect_action_content_replace']['field_address_email']);

return _phptemplate_callback('connect_edit_block', $vars);
*/

}
?>

I was soon able to determine that these were the lines I needed to add

<?php
        $form
['field_person_name_first']['0']['value']['#size'] = 15;
        
$form['field_person_name_last']['0']['value']['#size'] = 15;
?>

Finally

So that my final code looks like this:

<?php
function phptemplate_connect_form_block($form) {
  global 
$user;

//theme overriding code here 
        
$form['field_person_name_first']['0']['value']['#size'] = 15;
        
$form['field_person_name_last']['0']['value']['#size'] = 15;

//assign form input to variables
      
$vars = array('user' => $user'form' => $form);

      

$vars['fname'] = drupal_render($form['connect_action_content_replace']['field_person_name_first']);
      
$vars['lname'] = drupal_render($form['connect_action_content_replace']['field_person_name_last']);
      
$vars['email'] = drupal_render($form['connect_action_content_replace']['field_address_email']);

// callback 
return _phptemplate_callback('connect_edit_block'$vars);
}
?>

My end result was that the form input fields were now had a length of 15 instead of the 60 which it defaults to and looked much better in the website's sidebars where it was configured to work as a block.

Here are a list of links that were crucial in my understanding of all of this:

http://www.lullabot.com/articles/modifying-forms-5-and-6

http://drupal.org/node/187239

http://drupal.org/node/165706

http://drupal.org/node/264859

http://drupal.org/project/formfilter

http://drupal.org/node/173880

http://drupal.org/node/173880

http://api.drupal.org/api/file/developer/topics/forms_api.html/5

Section: 

Code: 

Comments

Why not just theme the fields in CSS?

While this would be a helpful technique for more serious customization of the form, if you just need to resize the fields, why wouldn't you just use CSS to style the fields? The "number of characters" size of an INPUT element is overridden by a css rule specifying width in px or em.

You can target the fields inside of blocks by using Drupal's many classes that get added to blocks.

so a rule like this:

.block-module_name .form-item input.someClass {
width: 40px;
}

true

Good point,

Perhaps I should have made this clearer, but, the purpose of this blog entry is mainly to illustrate the technique of overriding theme functions. The actual example used does not serve any useful purpose other than as a demonstration.

Certainly CSS is a simpler(and better) solution for something such as tweaking style attributes. The technique outlined above is better suited for cases where one wants to harness the power of PHP code, such as changing a style property based on the time of day or perhaps adding some logic which alters the default markup that Drupal outputs.

Varun

Add new comment