Now that we've defined our Data Layer with the field hooks we can move on to widgetry. We're only going to assemble one widget for this part, the manual input widget. We will cover advanced widgets in Part 4. We've got three hooks to work with again hook_widget_info, hook_widget_settings, and hook_widget.
You do not have to implement widgets with your field module, but you should probably provide at least one widget by default.
hook_widget_info
hook_widget_info is a little more complicated than hook_field_info. It returns a structured array containing a label and the fieldtypes it works with, keyed on the machine readable name for our widget. Be careful not to duplicate machine readable names for widget, content.module uses this key for indexing all widgets and all but one will be overridden if there are duplicates.
<?php
/**
* Implementation of hook_widget_info().
* @return array (
* 'widgettype' => array(
* 'label' => 'string',
* 'field types' => array('fieldtype', ...)
* )
* )
*/
function referralfield_widget_info() {
return array(
'referralfield_manual' => array(
'label' => 'Manual Input',
'field types' => array('referralfield'),
),
);
}
?>hook_widget_settings
hook_widget_settings provides the settings per field instance for a widget. You can scan through the Field Hooks for mentions of widget_settings, a quick search of part one reveals to me:
<?php
$err_field = $field['field_name'].']['.$delta.']['. $field['widget_settings']['imageurl_fieldname'];
$err_msg = t('The') .' '. $field['widget_settings']['imageurl_fieldtitle'] .' '. t('is required');
?>Any widget for our ReferralField must provide the 'imageurl_fieldname' and 'imageurl_fieldtitle'. We are using these values to send the widget form key and label for image urls to the field layer for validation. The hook itself has only 3 ops form, validate, and save. form and validate
are good old FormAPI constructs. The save hook is a little different from usual, you return the form keys you want Content.module to store in your widget's settings.
For this particular case we don't really need any user input since we're returning extended data about the structure of the widget to the Field, so we'll just throw a couple hidden values out there.
<?php
/**
* Implementation of hook_widget_settings().
*/
function referralfield_widget_settings($op, $widget) {
switch ($op) {
// settings form to be displayed during field creation.
case 'form':
$form = array();
$form['imageurl_fieldname'] = array (
'#type' => 'hidden',
'#value' => 'imageurl',
);
$form['imageurl_fieldtitle'] = array (
'#type' => 'hidden',
'#value' => 'Image Url',
);
return $form;
// settings form validation.
case 'validate':
break;
// form keys to save for settings form.
case 'save':
return array('imageurl_fieldname', 'imageurl_fieldtitle');
}
}
?>hook_widget
This is the workhorse callback for our widget. Here we will generate the forms for input, and handle the user input before passing it on to the Field. We've got five ops for hook_widget form, prepare form values, validate, process form values, and submit.
The form returned from the form op will extend the node form. Prepare form values is our opportunity to save files, interact with remote services, etc. Validation is input validation as usual. Process form values is a chance to modify submitted data before the field validate and submit callbacks get invoked.
We're only going to implement op = form, op = validate, an op = process form values for the manual widget.
Note: In the form callback there is a loop foreach($items as $delta => $item). This is a result of CCK's multiple field value support. In order to maintain consistency both single and multivalue fields are bundled as an array. You will find this construct often in field module. There will be a more in-depth of multi value fields in an addendum.
Our widget needs to return a value for each of the 'database columns' we implemented in hook_field_settings. This is the second part of our API. For the manual widget they will be implemented directly as for elements. In a later section we will look at using 'prepare form values' and 'process form values' to create more intelligent widgets.
<?php
/**
* Implementation of hook_widget().
*/
function referralfield_widget($op, $node, $field, &$items) {
$fieldname = $field['field_name'];
switch ($op) {
case 'form':
$form = array();
// Create our form and set #tree == true so the $item structure will be maintained
// for later usage in the process form values op.
$form[$fieldname] = array(
'#type' => 'fieldset',
'#title' => t($field['widget']['label']),
'#weight' => $field['widget']['weight'],
'#collapsible' => TRUE,
'#collapsed' => FALSE,
'#tree' => TRUE,
);
// Iterate through field values.
foreach($items as $delta => $item) {
$form[$fieldname][$delta] = _referralfield_widget_manual_item_form($item);
}
if ($field['multiple']) {
$form[$fieldname][$delta+1] = _referralfield_widget_manual_item_form();
$form[$fieldname][$delta+2] = _referralfield_widget_manual_item_form();
}
elseif (count($items) == 0) {
$form[$fieldname][0] = _referralfield_widget_manual_item_form($item);
}
return $form;
case 'prepare form values':
return;
case 'validate':
return;
case 'submit':
return;
case 'process form values':
// Don't save empty values, beyond the first value.
foreach ($items as $delta => $item) {
if ($item['value'] == '' && $delta > 0) {
unset($items[$delta]);
}
}
break;
return;
}
}
/**
* Helper function to generate forms for each $item.
* Note: The form element keys are the same as our database column names from
* the field implementation
*/
function _referralfield_widget_manual_item_form($item = array()) {
$form['description'] = array(
'#type' => 'textarea',
'#title' => t('Description'),
'#default_value' => $item['description'],
'#required' => TRUE,
);
$form['description_format'] = filter_form($item['description_format'], 1, array('description_format'));
$form['imageurl'] = array(
'#type' => 'textfield',
'#title' => t('Image URL'),
'#default_value' => $item['imageurl'],
);
$form['targeturl'] = array(
'#type' => 'textfield',
'#title' => t('Target URL'),
'#default_value' => $item['targeturl'],
'#required' => TRUE,
);
return $form;
}
?>We should now have a working widget for our ReferralField. The next section focuses on output handling with Formatters.