Creating Compound Fields with CCK

May 13
blog author

Appno Blogger

Appnovation Coop

When defining a Drupal content type, I continually come into the situation where I want to add fields to my content type as a group. That is, creating a CCK field called “Contact Info", which is a group containing Contact Name and Contact Email, for instance. With regular CCK this has to be done by adding several separate fields or by creating another content type for that “Contact Info” group. However, a while ago I learned how to create a compound CCK field that is essentially a group container of fields you define.

1. First step is to create a module. In this instance, I called my module, “contact_info.module”. I also created the .info file and a blank .install file.

2. Now, we'll give CCK the field's machine-readable name, human-readable name, and a longer description, by implementing the CCK hook_field_info().

/** * Implementation of hook_field_info */ function contact_info_field_info() {   return array(   'contact_information' => array(     'label' => t('Contact Information'),     'description' => t('Stores information about contacts'),   )   ); }

3. Next, we will implement the core Drupal hook_install(), hook_uninstall(), hook_enable(), and hook_disable() so that our field will be properly added and removed from Drupal and CCK. I added these functions to my .install file but you can place them in your module if you like.

/** * Implementation of hook_install */ function contact_info_install() {   drupal_load('module', 'content');   content_notify('install', 'contact_info'); } /** * Implementation of hook_uninstall */ function contact_info_uninstall() {   drupal_load('module', 'content');   content_notify('uninstall', 'contact_info'); } /** * Implementation of hook_enable */ function contact_info_enable() {   drupal_load('module', 'content');   content_notify('enable', 'contact_info'); } /** * Implementation of hook_disable */ function contact_info_disable() {   drupal_load('module', 'content');   content_notify('disable', 'contact_info'); } 4. Now, we need to tell CCK about the "settings" for this field, using CCK hook_field_settings(). /** * Implementation of hook_field_settings. */ function contact_info_field_settings($op, $field) {   switch ($op) {     case 'database columns':     $columns['name'] = array('type' => 'text', 'not null' => TRUE);     $columns['email'] = array('type' => 'text', 'not null' => TRUE);     return $columns;   } }

5. The next set of things we need to define is included in CCK hook_field(), which basically tells CCK if it needs to do anything special when a field is loaded from or saved to the database

/** * Implementation of hook_field. */ function contact_info_field($op, &$node, $field, &$items, $teaser, $page) {   switch ($op) {     case 'validate':     break;   case 'sanitize':     foreach ($items as $delta => $item) {       foreach ($item as $col => $dat) {         $items[$delta]['safe_' . $col ] = check_plain($item[ $col ]);       }     }     break;   } }

6. The next piece of information CCK needs about our fields is how to tell if it is "empty" of information (CCK hook_content_is_empty()),

/** * Implementation of hook_content_is_empty. */ function contact_info_content_is_empty($item, $field) {   //The whole field is only considered empty if no issue is added   if (empty($item['name'])) {     return TRUE;   }   return FALSE; }

7. Now that we have the CCK field itself defined, our next task is to define a "widget", which is the CCK name for a form used to edit the field. Our Contact Info widget will just need to let us enter text for the name and email address.

/** * Implementation of hook_widget_info */ function contact_info_widget_info() {   return array(   'contact_information_entry' => array(     'label' => t('Contact Information'),     'field types' => array('contact_information'),     'multiple values' => CONTENT_HANDLE_CORE,     'callbacks' => array(     'default value' => CONTENT_CALLBACK_DEFAULT,     ),   ),   ); }

8. Next, we need to define the widget's data entry form and how to process it. This is done using the core Forms API hook_elements(), which returns an associative array with an element whose key is the machine-readable name of our widget, and whose value is an associative array that either defines the form completely, or gives a "process" callback function and some other information.

/** * Implementation hook_elements. */ function contact_info_elements() {   $elements = array('contact_information_entry' =>    array(      '#input' => TRUE,      '#process' => array('contact_info_issue_response_entry_process'),    ),   );   return $elements; }

9. Since I used the "process" method in hook_elements above, I need to define the process function.

/** * Process callback for widget * * Returns a Forms API array that defines the widget's editing form. */ function contact_info_issue_response_entry_process($element, $edit, &$form_state, $form) {   $defaults = $element['#value'];   $field = content_fields($element['#field_name'], $element['#type_name']);   $element['name'] = array(     '#type' => 'textfield',     '#title' => 'Contact Name',     '#default_value' => $defaults['name'],     '#weight' => 0,   );   $element['email'] = array(     '#type' => 'textfield',     '#title' => 'Contact Email',     '#default_value' => $defaults['email'],     '#weight' => 1,   );   return $element; }

10. In order to display the editing widget, we need to define a theme function, theme_(widget), which is called as an envelope for each item when it is added to the node editing form.

/** * Implementation of hook_theme(). */ function contact_info_theme() {   return array(   'contact_information_entry' => array(     'arguments' => array('element' => NULL),   ),   'theme_contact_info_formatter_default' => array(     'arguments' => array('element' => NULL),   ),   ); } /** * FAPI theme for an individual text elements. */ function theme_contact_information_entry($element) {   return $element['#children']; } /** * Theme function for default formatter. */ function theme_contact_info_formatter_default($element = NULL) {   if(empty($element['#item'])) {     return '';   }   $stuff = $element['#item'];   $flds = array('name', 'email');   $ret = '

'   $sep = '';   foreach($flds as $fld) {   if(!empty($stuff['safe_' . $fld ])) {     $ret .= $sep . '' . $stuff['safe_' . $fld ] . '';     $sep = "
\n";   }   }   $ret .= '

';   return $ret; }

11. The next hook we need to implement in order to define the editing widget is CCK hook_widget(). This hook will be called each time one of our fields is added to the form, with the $delta parameter set to the multiple-value index

/** * Implementation of hook_widget */ function contact_info_widget(&$form, &$form_state, $field, $items, $delta = 0) {   $element = array(   '#type' => $field['widget']['type'],   '#default_value' => isset($items[$delta]) ? $items[$delta] : '',   );   return $element; }

12. Having now defined our field and the editing widget, the final thing we need to do is to define how it will look to someone who is visiting the site

/** * Implementation of hook_field_formatter_info * * Returns information about available field formatters. */ function contact_info_field_formatter_info() {   return array(   'default' => array(     'label' => t('Contact Information'),     'field types' => array('contact_information'),   ),   ); }

Once this is complete, you can go into Drupal and enable your compound field module. Now, you'll be able to add the field you've defined by selecting it from the CCK drop down field within "Manage Fields".