Upgrade Your Drupal Skills

We trained 1,000+ Drupal Developers over the last decade.

See Advanced Courses NAH, I know Enough

Creating a CCK Field module with an "invisible" widget

Parent Feed: 

Most CCK Field modules have a widget where the user adds information upon creating a node which is then saved with the node. For the recent Etherpad module I wrote, I needed an "invisible" widget which saved with each new node some information from the field definition as well as autogenerated information. As I didn't any documentation on how to do this, I thought I'd document it here quickly.

The first thing you do is define your database columns for your field in hook_field_settings.

<?php
function etherpad_field_settings($op, $field) {
  switch ($op) {
    
    case 'database columns':
      return array(
        'etherpad_url' => array('type' => 'varchar', 'length' => 1024, 'not null' => FALSE,),
        'etherpad_text' => array('type' => 'text', 'not null' => TRUE, 'size' => 'big'),
        'attributes' => array('type' => 'text', 'size' => 'medium', 'not null' => FALSE),
      );
  }
}
?>

Next, you define your widget form inside hook_widget. Some tutorials I saw suggest you define your widget form in hook_elements/hook_process. I did that at first but decided against it as a) I never got it to work and b) it just adds needless complexity. Generally you'll just want to define your widget in hook_widgets.

Two really important things here to get your "invisible" widget to work correctly. First, you must name your form keys the same as you named your database column names in hook_field_settings. This tripped me up for a long time. CCK saves data by magic (you never explicitly save anything from a widget) and this is the key to getting the incantation to take. Second, using the "value" field type was the key to creating an "invisible" field and getting my data saved correctly.

<?php

function etherpad_widget(&$form, &$form_state, $field, $items, $delta = 0) {
  $element['etherpad_url'] = array(
    '#type' => 'value',
    '#value' => (isset($items[$delta]['etherpad_url']) && !empty($form['nid']['#value'])) ? $items[$delta]['etherpad_url'] : $field['etherpad_url'] . etherpad_generate_padid($field['etherpad_url']),
  );
  $element['etherpad_text'] = array(
    '#type' => 'value',
    '#value' => (isset($items[$delta]['etherpad_text']) && !empty($form['nid']['#value'])) ? $items[$delta]['etherpad_text'] : "default value for now until we have a function to generate one",
  );
  $element['attributes'] = array(
    '#type' => 'value',
    '#value' => (isset($items[$delta]['attributes']) && !empty($form['nid']['#value'])) ? $items[$delta]['attributes'] : serialize($field['attributes']),
  );
  
  
  if (empty($form['#parents'])) {
    $form['#parents'] = array();
  }
  $element['_error_element'] = array(
    '#type' => 'value',
    '#value' => implode('][', array_merge($form['#parents'], array('value'))),
  );

  return $element;
}
?>

And that's it! Read this and you'll save yourself hours of frustration :)

One other note, the Devel module's "reinstall module" function is very useful as you'll be reinstalling the module often to reset the database w/ your changes. Enable the Devel block to access it.

This documentation doesn't cover most of what you'll need to know to write a CCK module. I relied heavily on the following tutorials.

Author: 
Original Post: 

About Drupal Sun

Drupal Sun is an Evolving Web project. It allows you to:

  • Do full-text search on all the articles in Drupal Planet (thanks to Apache Solr)
  • Facet based on tags, author, or feed
  • Flip through articles quickly (with j/k or arrow keys) to find what you're interested in
  • View the entire article text inline, or in the context of the site where it was created

See the blog post at Evolving Web

Evolving Web