You are here

Drupal 7 - A Simple Content Type: Adding Checkboxes and Radio Buttons

This post builds upon a previous post: Drupal 7 - A Simple Content Type

Being able to create a simple content type with a title and a body has limited re-use capacity since there are already two content types that ship with Drupal 7 which are already set up that way: Pages and Articles. So let's add another field to our content type: a set of checkboxes, which can also double as radio buttons with minor changes. We need to add some code to our install hook so go ahead and fully uninstall the Simple Content Type module from previous examples if it is currently enabled. Next we need to add our new checkbox field to the content type and configure the way instances of the new checkbox field should behave. Here is our updated hook_install() function:


function simple_content_type_install() {
  // create the simple content type
  node_types_rebuild();
  $types = node_type_get_types();
  
  // add the body field to the node type
  node_add_body_field($types['simple_content_type']);
  // load the instance definition for our content type's body
  $body_instance = field_info_instance('node', 'body', 'simple_content_type');
  // configure the body field
  $body_instance['type'] = 'text_summary_or_trimmed';
  $body_instance['label'] = 'Simple Description';
  $body_instance['display'] = array(
    'default' => array(
      'label' => 'above', 
      'type' => 'text_default',
      'weight' => 0,
    ),
    'teaser' => array(
      'label' => 'hidden', 
      'type' => 'text_summary_or_trimmed',
      'weight' => 0,
    ),
  );
  $body_instance['widget']['weight'] = 0;
  
  // save our changes to the body field instance
  field_update_instance($body_instance);
  
  // create all the fields we are adding to our content type
  foreach (_simple_content_type_installed_fields() as $field) {
    field_create_field($field);
  }
  
  // create all the instance for our fields
  foreach (_simple_content_type_installed_instances() as $instance) {
    $instance['entity_type'] = 'node';
    $instance['bundle'] = 'simple_content_type';
    field_create_instance($instance);
  }
  
  // disable comments for this content type
  variable_set('comment_simple_content_type', COMMENT_NODE_CLOSED);
}

We use two foreach statements coupled with functions that return associative arrays to make the task of adding fields to our content type easy. We could just stuff the settings into the install function seeing as we only have one field to add and configure but this makes the code more readable. We next need to modify the uninstall hook to delete any fields we created for our content type. Here is our updated hook_uninstall() function:


function simple_content_type_uninstall() {
  // gather all the content while the module was enabled
  $sql = 'SELECT nid FROM {node} n WHERE n.type = :type';
  $result = db_query($sql, array(':type' => 'simple_content_type'));
  $nids = array();
  foreach ($result as $row) {
    $nids[] = $row->nid;
  }
  
  // delete all the nodes at once
  node_delete_multiple($nids);
  
  // delete all the fields defined by this module
  foreach (array_keys(_simple_content_type_installed_fields()) as $field) {
    field_delete_field($field);
  }
  
  // delete any remaining field instances attached to this content type
  $instances = field_info_instances('node', 'simple_content_type');
  foreach ($instances as $instance_name => $instance) {
    field_delete_instance($instance);
  }
  
  // delete our content type
  node_type_delete('simple_content_type');
  
  // purge all field information
  field_purge_batch(1000);
}

Both of the functions we are using in the above hooks reside in a new file called 'simple_content_type_functions.inc'. This file will hold all the non-hook functions we code for use in our hooks. In order to use the functions in this file we need to add the new functions file to the .install and .module files:


require_once('simple_content_type_functions.inc');

Without adding this file include to the .module file, even though it is not directly referenced there, our content type will not be able to find the function we set-up to populate the array holding our options list. Our new file, simple_content_type_functions.inc, has three functions:


function _simple_content_type_installed_fields()  {
  // checkboxes
  $fields['simple_content_type_checkboxes'] = array(
    'field_name' => 'simple_content_type_checkboxes',
    'label' => t('Select some checkboxes'),
    'type' => 'list_integer',
    'cardinality' => '-1', // change this to 1 to get radio buttons instead
    'foreign keys' => array(),
    'indexes' => array(
      'value' => array(
        0 => 'value',
      ),
    ),
    'module' => 'list',
    'settings' => array(
      'allowed_values_function' => '_simple_content_type_options_list',
    ),
  );
  
  return $fields;
}

function _simple_content_type_installed_instances()  {
  // checkboxes
  $instances['simple_content_type_checkboxes'] = array(
    'field_name' => 'simple_content_type_checkboxes',
    'label' => t('Checkboxes'),
    'description' => t('Select some checkboxes'),
    'default_value' => NULL,	// add a default value here that matches your key => index values
    'display' => array(
      'default' => array(
        'label' => 'above',
        'module' => 'list',
        'settings' => array(),
        'type' => 'list_default',
        'weight' => -1,
      ),
      'teaser' => array(
        'label' => 'above',
        'settings' => array(),
        'type' => 'hidden',
        'weight' => -1,
      ),
    ),
    'required' => 1,
    'settings' => array(
      'user_register_form' => FALSE,
    ),
    'widget' => array(
      'active' => 1,
      'module' => 'options',
      'settings' => array(),
      'type' => 'options_buttons',
      'weight' => '-1',
    ),
  );
  
  return $instances;
}
/**
 * Options callback for simple_content_type_checkboxes field
 * @return - returns an indexed array as integer => string
 */
function _simple_content_type_options_list()  {
  $options = array(
    1 => 'Checkbox A',
    2 => 'Checkbox B',
    3 => 'Checkbox C',
    4 => 'Checkbox D',
    5 => 'Checkbox E',
    6 => 'Checkbox F',
  );
  return $options;
}

We are setting our checkbox field 'type' as a list of integers which allows us to create key => value pairs like you see in the '_simple_content_type_options_list()' function using an integer as the key in the pair. We could also use the 'list_text' type if we wanted to use strings as the keys in our pairs. The fields settings also help to determine whether our field will be displayed as checkboxes or radio buttons; by setting the 'cardinality' to '1' we tell Drupal that our field only accepts one single value and it should use radio buttons, setting it to '-1' tells Drupal that the user can select multiple options and so should use checkboxes for display. Our instances functions sets the widget, display, and a host of other settings which affect how our field will be displayed to the user. I won't delve too much into the Field UI here, we'll save that for another time. Lastly, the function we set up to populate our options list creates an associative array and returns it when called.

And there you have it: a simple content type with a set of checkboxes displaying static options. To make our checkbox/radio button options dynamic, we change _simple_content_type_options_list() in our functions file:


function _simple_content_type_options_list()  {
  $vocab = taxonomy_vocabulary_machine_name_load('andy_pangus');
  $vid = $vocab->vid;
  $sql = 'SELECT tid, name FROM {taxonomy_term_data} t WHERE t.vid = :vid';
  $results = db_query($sql, array(':vid' => $vid));
  
  $options = array();
  foreach ($results as $term)  {
    $options[$term->tid] = $term->name;
  }
  return $options;
}

The updated function creates an associative array from a list of vocabulary terms which could contain few or many terms that could be added or deleted at any time. I have created a helper module called Simple Vocab to provide a set of terms for us to use in the dynamic example. The simple_vocab module contains code that creates a vocabulary, machine name: andy_pangus, and adds some terms to it. This module is required for the dynamic example to work and is included in the download below.

Download the source for the static example.

Download the source for the dynamic example.

Tags: