We begin our Journey in to CCK 1.x Wonderland by defining a field. Developing a CCK Field requires working with three systems of hooks: Fields(Data Model), Widgets(User Interfaces/Field Input), and Formatters(Ouput).
We're going to start with Field hooks, since they let CCK know we offer a field.
hook_field_info
This hook lists the fieldtypes implemented in our module. It returns an associative array of machine readable names and human readable labels for each of the field types implemented in the module.
<?php
/**
* Implementation of hook_field_info().
*
* @return array ('fieldtype' => array('label' => 'example'))
* the only key for the fieldtype array is label.
*/
function referralfield_field_info() {
return array(
'referralfield' => array('label' => 'Referral Link'),
);
}
?>Taking a moment of repose let us think about the data needs of our referralfield.module.
Description, target URL, and image URL are user input, so they will be kept in the database. Image required will be for this particular field instance and stored in the widget settings.
<?php
/**
* Implementation of hook_field_settings().
* @param op
* one of form, filters, validate, save, database columns
* @param field
* the field we are working on
*/
function referralfield_field_settings($op, $field) {
switch ($op) {
// This will be included on the add field form and
// will be shared across all instances of this field.
case 'form':
$form = array();
$form['requireimage'] = array(
'#type' => 'checkbox',
'#title' => t('Require Images for the Referral Field'),
'#default_value' => $field['settings']['requireimage'],
);
return $form;
// this will be called when the field settings form is being validated,
// before the widget settings form is displayed.
case 'validate':
break;
// these are the key values to save from the field settings form.
// They will be used in the form's submit handler
case 'save':
return array('requireimage');
// database columns op return the db structure required to store this field.
// *cck has it's own little DDL. They're simple string replacements in an SQL query.
case 'database columns':
$columns = array(
'description' => array(
'type' => 'text',
'not null' => TRUE,
'default' => "''"
),
'description_format' => array(
'type' => 'int',
'not null' => TRUE,
'default' => 0
),
'imageurl' => array(
'type' => 'varchar',
length => 255,
'not null' => TRUE,
'default' => "''", 'sortable' => FALSE
),
'targeturl' => array(
'type' => 'varchar',
'length' => 255,
'not null' => TRUE,
'default' => "''",
'sortable' => TRUE
),
);
return $columns;
}
}
?>So far we have let content.module know that our module implements a field type with hook_field_info, and
we've defined its data requirements and options through hook_field_settings.
Finally, lets think about our workhorse hook for fields... hook_field. This is the actual data manipulation stuff. It should provide a fairly light interface, since widgets should be doing and the fun stuff and passing data to this layer.
hook_field
This hook allows us to do any field dirty work that needs to be done with the data we receive from widgets that is headed for content.module. This allows you to store data in your own tables, with external services, or execute auxillary functions before content.module has its way with your field. It also allows you replace data coming from content.module with data you've stored externally on the load op.
<?php
/**
* Implementation of hook_field().
* @param op
* the operation
* @param node
* the node we're working with.
* @param field
* the field we're working on with its settings.
* @param items
* the data for this field
* @param teaser
* are we a tease?
* @param page
* is the spotlight on us?
*/
function referralfield_field($op, $node, $field, &$items) {
$fieldname = $field['field_name'];
switch ($op) {
// content.module has already loaded the data from our 'database columns'
// for us in itemd. if we need or override the data we have in items
// (an nid or fid, or some reference) we do it here.
// @return array('fieldname' => array('key' => 'value'))
case 'load':
break;
// This is where we define a default view these days. If you wish to shortcut
// the formatter system you'd do it here. Formatters are a very useful power
// output mechanism. I suggest you use it. This hook may be deprecated in the
// future.
case 'view':
break;
// Validate the data we recieved from our widgets.
case 'validate':
if ($field['global_settings']['imagerequired']) {
foreach ($items as $delta => $item) {
if (!isset($item['imageurl'])) {
// we'll need to construct some way to get this fieldname
$err_field = $field['field_name'].']['.$delta.']['. $field['widget_settings']['imageurl_fieldname'];
$err_msg = t('The') .' '. $field['widget_settings']['imageurl_fieldtitle'] .' '. t('is required');
form_set_error($err_field, $err_msg);
}
}
}
break;
// we're submitting this node. This isn't a preview.... Do whatcha gotta do.
case 'submit':
break;
// Here you get a chance to save data before content.module puts data in your
// 'database columns'. You could create a node and pass an nid back, save a file,
// query an external service and overwrite node_data['key'] with a foreign key value,
// write data to your own database table and return its key...
case 'insert':
break;
// Here again you get to alter the data coming from the widget before it gets to the
// content.module on update..
case 'update':
break;
// You get this before content.module has a chance to delete, so you can stop
// the process if there are any problems.
case 'delete':
break;
}
return $output;
}
?>So we've let CCK know we define a field, told cck what we need to configure the field, and what we need in terms of data storage. We've even told CCK what to do in the node life cycle the data layer figures into.
I should also point out we defined an 'interface' for our widgets to work with. Looking back over the code you can determine the data required from widgets settings forms by looking for $field['widget_settings']. You can also determine the data widgets must provide to fields examining hook_field_settings op 'database columns' and hook_field. If hook_field's CRUD functions are manipulating data provided by the widget form, we provide the widget must provide form fields that hook_field expects, other wise you just provide form fields named and types as per the fields database columns.
Next installment will be widgetry!
Comments
Hello
Thanks for sharing!