Drupal 7 Field API - A simple example
The following example uses Drupal 7 beta 3 release. A simple introduction and demonstration of the new Field API (CCK rolled in the Core Drupal API).
When you install Drupal 7 beta 3, the system is pre-configured with several base field types (e.g. Decimal, Boolean, Integer, Image. etc). An adminstrator add one of the forementioned field types to a content type (e.g. Story or Page) and thus create a "custom" content type. An admistrator simply uses the adminstrative user interface (web forms) to create a custom content type. So far, no programming required.
For our example, we decided to add a new field type, using the new Drupal 7 Field API. We decided to call our field "News Date". It's a simple field that is stored in the database as an integer (number of seconds), The field is displayed in M-d-Y format (e.g. Nov-17-2010). When you create or edit the "News Date" field, the system displays the standard Drupal 3 drop down combo boxes (month, date, year).
To create our module. we start by creating a new folder (news_date) with 3 files: news_date.info, news_date.install and news_date.module.
We define the database storage in the news_data.install file:
<?php function news_date_field_schema($field) { return array( 'columns' => array( 'news_date' => array( 'type' => 'int', 'not null' => TRUE, 'default' => 0, ), ), ); }
We create a simple info file:
name = News Date Field description = A Simple Date Field for Drupal 7 package = public-action fields version = VERSION core = 7.x files[] = news_date.module files[] = news_date.install
And finally a simple module file which implements a few hooks:
<?php /** * Implements hook_field_info(). */ function news_date_field_info() { return array( 'news_date' => array( 'label' => t('News Date'), 'description' => t('A Date Time Period'), 'default_widget' => 'news_date_widget', 'default_formatter' => 'news_date_formatter', ), ); } /** * Implements hook_field_formatter_info(). */ function news_date_field_formatter_info() { return array( 'news_date_formatter' => array( 'label' => t('Simple News Date formatter'), 'field types' => array('news_date'), ), ); } /** * Implements hook_field_formatter_view(). */ function news_date_field_formatter_view($entity_type, $entity, $field, $instance, $langcode, $items, $display) { $element = array(); switch ($display['type']) { case 'news_date_formatter': foreach ($items as $delta => $item) { if ($item['news_date']) { $formattedDate = _dateToString($item['news_date']); $element[$delta]['#markup'] = '<span>' . $formattedDate .'</span>'; } } break; } return $element; } /** * Implements hook_field_widget_form(). */ function news_date_field_widget_form(&$form, &$form_state, $field, $instance, $langcode, $items, $delta, $element) { $field_name = $field['field_name']; $field_type = $field['type']; $default_date_int = $instance['default_value'][0][$field_type][$field_type]; $default_date_array = _intToDateArray($default_date_int); $news_data_value = isset($default_date_array) ? $default_date_array : _intToDateArray(time()); if (isset($form_state['input'][$field_name][$langcode][$delta][$field_type]['day'])) { $day = $form_state['input'][$field_name][$langcode][$delta][$field_type]['day']; $month = $form_state['input'][$field_name][$langcode][$delta][$field_type]['month']; $year = $form_state['input'][$field_name][$langcode][$delta][$field_type]['year']; $news_data_value = array('year' => $year, 'month' => $month, 'day' => $day); } switch ($instance['widget']['type']) { case 'news_date': $element['news_date'] = array( '#type' => 'date', '#title' => $element['#title'], '#description' => $element['#description'], '#default_value' => $news_data_value, '#required' => $element['#required'], '#weight' => isset($element['#weight']) ? $element['#weight'] : 0, '#delta' => $delta, '#element_validate' => array('strToDateValidate'), ); break; } return $element; } /** * Implements hook_field_widget_error(). */ function news_date_widget_error($element, $error, $form, &$form_state) { switch ($error['error']) { case 'news_date_invalid': form_error($element, $error['message']); break; } } /** * Implements hook_field_is_empty(). */ function news_date_field_is_empty($item, $field) { if (empty($item['news_date'])) { return true; } } /** * Implements hook_field_widget_info(). */ function news_date_field_widget_info() { return array( 'news_date' => array( 'label' => t('News Letter Date'), 'field types' => array('news_date'), ), ); } /** * Utility function which converts the 3 elements, month, day and year * in to a unix timestamp (number of seconds) */ function strToDateValidate($element, &$form_state) { if (isset($element['#value'])) { $day = $element['#value']['day']; $month = $element['#value']['month']; $year = $element['#value']['year']; $value = mktime(0, 0, 0, $month, $day, $year); form_set_value($element, array('news_date' => $value), $form_state); } } /** * Utility function which converts a string representation of a unix timestamp * and converts to a human readable format. */ function _dateToString($dateValue) { $str = "Unable to Format Date"; try { $timestamp = intval($dateValue); $str = format_date($timestamp, 'custom', 'M-d-Y'); } catch (Exception $e) { $str = "Unable to Format Date" . $e->getMessage(); } return $str; } /** * Utility function which converts a string representation of a unix timestamp * and converts to an array suitable for the Drupal date widget (3 drop down combo * boxes, month, day, year). */ function _intToDateArray($timestr) { $timestamp = intval($timestr); $year = date('Y', $timestamp); $month = date('m',$timestamp); $day = date('d',$timestamp); return array('year' => $year, 'month' => $month, 'day' => $day); }
Quick explanation:
The news_date.install file includes a hook_field_schema implementation. The implementation tells drupal to store our field value in the database as an integer.
The rest of the methods simply convert the integer (news_date_field_formatter_view()) to a date for display.
news_date_field_widget_form() instructs Drupal to use a "date" widget when creating or editing our field.
The statement '#element_validate' => array('strToDateValidate'), instructs Drupal to convert the widget data (month, day, year) back to an integer (number of seconds).




Comments
Few remarks
Hi,
Nice script and basis to build on. A few remarks:
I'd rather use the current date instead of a default date and want to be able to retrieve a previous saved date from the db, so I changed news_date_field_widget_form() from:
$default_date_int = $instance['default_value'][0][$field_type][$field_type];
$default_date_array = _intToDateArray($default_date_int);
$news_data_value = isset($date_array) ? $date_array : _intToDateArray(time());
to
$default_date_int = isset($items[$delta][$field_type]) && !is_array($items[$delta][$field_type]) ? $items[$delta][$field_type] : time();
$news_data_value = _intToDateArray($default_date_int);
But also, now I load previous set dates, the did not get selected in the HTML select element, because _intToDateArray() returned month and day numbers with leading zeros. So I changed this function from:
$month = date('m',$timestamp);
$day = date('d',$timestamp);
to
$month = date('n',$timestamp);
$day = date('j',$timestamp);
Nice way to learn how to make custom fields! :-)
Cheers!
Laurens Meurs, Rotterdam
Nice tutorial
Nice tutorial, but how do you associate it to a node / entity? Could you add a tutorial on how to add this new field to something please. I assume you use the field_attach.api but I cant find any examples that go through the whole create field -> attach field to something. Cheers love the site :-)
My bad I see the follow-up blog has the answer :-)
I see the answer is here in this post.
http://public-action.org/content/drupal-7-field-api-drupal-7-adding-cust...
However how would you add it to say
user/%/edit page?
Change the entity type to user
If you takes the code in http://public-action.org/content/drupal-7-field-api-drupal-7-adding-cust... and change the entity type to user, wouldn't that do it?
In Drupal 7 both Nodes and User objects are specializations of Entity. Thus replacing an entity type of "node" with an entity type of "user" should suffice.