Brought to you by molecularsciences.org.
This work is licensed under a Creative Commons Attribution-Share Alike 3.0 License.
This publication may not be redistributed without this notice.

Drupal Forms

forms

making your checkboxes preselected in edit forms

Imagine this scenario. You create an add form with checkboxes among other fields. When the user submits, your form stores this information in a database table. Then the user decides to edit his selection. He goes to the edit form. There he sees his previous choices already selected. This modifies his selection and his modifications are registered on a database table.

This simple scenario is quite complicated to implement in drupal. When faced with this problem, I rigorously searched through the drupal documentation and blogs to find the solution. Unfortunately, I couldn't find anything. Then I looked into the existing drupal core modules and imitated their functions. I wrote this page so that others don't have to spend as much time as I did on this problem.

Checkbox data is stored in an associative array. The format of the associative array is Array(id=>name, id2=>name2, ...) by default. The checkboxes are populated by the value of #options attribute. The #default_value attribute defines the default value(s). The default values are unchecked for each checkbox.

Once a form is processed, drupal merges all keys and values. To make an editable format, we would be exploiting this property. We will pass Array(id=>value,...) to #options and nothing to #default_value. In the edit form, we would pass Array(name=>name) to #options and Array(id=>name) to #default_value. When the form array is merged, all values with id=>name will replace their corresponding name=>name pairs.

Thus we end up with a list of all option displayed and chosen ones checked.

/**
 * Implementation of hook_form().
 */ 
function projects_form(&$node) {
  // project title
  $form['title'] = array(
    '#type' => 'textfield',
    '#title' => t('Project Title'),
    '#required' => TRUE,
    '#default_value' => $node->title,
    '#weight' => -5
  );

  // project description
  $form['body_filter']['body'] = array(
    '#type' => 'textarea', 
    '#title' => t('Project Description'), 
    '#default_value' => $node->body, 
    '#rows' => 15, 
    '#required' => TRUE
  );
  $form['body_filter']['format'] = filter_form();

  // users sharing the project
  if (arg(1) == 'add') { 
    // returns Array(id=>name,id2=>name2 ...)
    $checklist = addformlist(); 
  } else { 
    // returns Array(name=>name,name2=>name2 ...)
    $checklist = editformlist(); 
    // returns Array(id=>name,id2=>name2 ...)
    $shred = previouslyselected($node->nid);
  }

  // my checkboxes
  $form['sharers'] = array(
    '#type' => 'checkboxes', 
    '#title' => t('Share with'), 
    '#default_value' => $shred, 
    '#options' => $checklist, 
    '#description' => t('some description.'), 
    '#required' => FALSE
  );

  return $form;
}

addformlist() return the options in Array(id=>name) format. editformlist() return a options in Array(name=>name) format. previouslyselected() returns a list of previously selected checkboxes in Array(id=>name) format. The important variables are the $checklist and $shred arrays. If you understand the format they expect and how to apply them, then that's all there is to it.

Modifying your forms in drupal

Drupal forms can be modified by the form alter hook. This performs alterations to your form before it is rendered. It is especially useful when you are writing a module which modifies another module. For example, a mypage.module could modify the page module by removing the comment and author fieldset along with the log message textarea and adding two new textfields.

First time users of hook_form_alter often get frustrated since their modifications work on certain fields and not others. This is due to the fact that drupal loads modules in alphabetical order and overriding modules, such as mypage.module mentioned above, must be loaded after the modules they override. There are two ways to solve this problem. You can change name of your module such that is loaded after the module you wish to override. A better way would be to go into the system table is the database and modify the weight of the module. All modules are assigned the weight zero by default. Therefore, modifying the weight of your module form 0 to 1 would assure that your module would be loaded in the desired order.

Modifying a form is a form step process:

Step 1: Define a _form_alter form

function mypage_form_alter($form_id, &$form) {

  return $form
}

The function name must end with _form_alter. The function takes $form_id and a reference to the $form array as parameters. This function returns the $form array reference after modifications.

Step 2: Define a _form_alter form

function mypage_form_alter($form_id, &$form) {
  if($form_id == 'mymodule_node_form') {

  }
  return $form
}

It is prudent to test for your form_id. This would serve to eliminate a number of difficult to debug errors. If you do not the name of your form, view the form source in the browser. It should look something like:

<input type="hidden" name="edit[form_id]" id="edit-mypage-node-form" value="mypage_node_form" />

Thus the form_id is mymodule_node_form.

Step 3: Modify or unset undesirable fields

function mypage_form_alter($form_id, &$form) {
  if($form_id == 'mymodule_node_form') {
    // modify attributes of existing fields
    $form['author'] = array('#type' => 'hidden');
    $form['options'] = array('#type' => 'hidden');

    // remove form fields
    unset($form['menu']);
    unset($form['path']);
  }
  return $form
}

In this example, I set two fieldsets to hidden. This has the same effect as removing a field from the user's point of view. However, the difference is significant from a module developer's point of view. Hidden fields allow a developer to pass defined values. unset() simply removes the form elements from the $form array.

Step 4: Adding new fields

function mypage_form(&$node) {
  $form['field1'] = array(
    '#type' => 'textfield', 
    '#title' => t('field1'), 
    '#required' => TRUE, 
    '#default_value' => $node->field1, 
    '#weight' => -5
  );
  $form['field2'] = array(
    '#type' => 'field2', 
    '#title' => t('field2'), 
    '#default_value' => $node->field2,
    '#required' => TRUE
  );
}

As you can see, adding fields to a form simply involves adding fields as you would add to a any drupal form. You just have to make sure that you add your field to the right form array. Page module uses node module forms. Therefore, we add to the $node array.

Beware

Node form fields post data in database tables. When you unset a field, a default value is posted in its place. For example, if you unset author, the uid field in node table would get a 0 value by default.

Making a text field uneditable

Every now and then, we need to show the data in a form field, pre-checked checkboxes, or pre-selected selection menus, which should not be editable by the user. Very often, these sort of functions are used in permissions and access control related pages.

In HTML, an uneditable form field can be created as follows:

<input type="text" readonly="readonly" name="cantedit" />

In drupal, an editable form field can be created as follows:

$form['name'] = array( '#type' => 'textfield', '#title' => t('name'), '#attributes' => array('disabled' => 'disabled'), );