Hello,
I would appreciate some help understanding the difference between a field and a widget, from the perspective of someone who is developing add-ons for CCK. Specifically, I have some questions regarding the role of each, and I have a suggestion for perhaps improving CCK's design.
More detail: Yesterday I spent the afternoon writing up CiviCRM reference fields for CCK. It was a pretty easy task (thanks for making my life easy!). I was able to copy and paste a lot of code from the userreference and nodereference modules. This got me to thinking, why copy and paste when the code could be factored out and combined in a common location?
I want to highlight some code at this point. Here is some of the userreference widget code:
switch ($op) {
case 'form':
$form = array();
$node_field = $node->$field['field_name'];
if ($field['multiple']) {
$node_field_transposed = content_transpose_array_rows_cols($node_field);
$uids = $node_field_transposed['uid'];
}
else {
$uids = array($node_field['uid']);
}
$form[$field['field_name']] = array('#tree' => TRUE);
$form[$field['field_name']]['uids'] = array(
'#type' => 'select',
'#title' => t($field['widget']['label']),
'#default_value' => $uids,
'#multiple' => $field['multiple'],
'#options' => _userreference_potential_references($field),
'#required' => $field['required'],
'#description' => $field['widget']['description'],
);
return $form;
And here is some of my code:
case 'form':
$options = array();
if (!$field['required']) {
$options[0] = '<' . t('None') . '>';
}
$options += _civicrmdata_dispatch($crm_entity, 'get');
$form = array();
$node_field = $node->$field['field_name'];
if ($field['multiple']) {
$node_field_transposed = content_transpose_array_rows_cols($node_field);
$crmids = $node_field_transposed['crmid'];
}
else {
$crmids = array($node_field['crmid']);
}
$form[$field['field_name']] = array('#tree' => TRUE);
$form[$field['field_name']]['crmids'] = array(
'#type' => 'select',
'#title' => t($field['widget']['label']),
'#default_value' => $crmids,
'#multiple' => $field['multiple'],
'#options' => $options,
'#required' => $field['required'],
'#description' => $field['widget']['description'],
);
return $form;
break;
Pretty similar, huh? The only real difference is that my code allows for a none option (perhaps something that should be in the userreference code?) and that we define the domain of the widget differently. I spent some time thinking about why these two pieces of code were so similar and if I could factor out the commonality.
Here is my conclusion: as long as widgets are responsible for defining the domain of a field, we cannot factor out the commonality.
Solution: I see two solutions to this problem.
- Solution 1: Fields define their own domains
-
In this solution, fields have an extra operation in hook_field_settings(), called 'domain'. In this operation, they return the domain of possible values they can contain. I would suggest a keyed array as a return value. We might also look at providing bounds and comparators for continuous field types.
In this scenario, we would need many fields (user reference, node reference, date, text box, etc), but few widgets. Widgets would have a callback where they would receive the data type and the domain for each field and they could indicate if they would work on it.
For starters we could have widgets to do single select, multiple select, normal textfields, and autocompletes. Given the data type and the domain, the widgets could properly format the form for input.
- Solution 2: Fields map precisely to db column types
-
In this solution (in my opinion the weaker of the two), we need few fields but many widgets. In this scenario, fields only hold specific data types. Widgets define the domain of the input.
We would need fields for int, string, date, file, etc. We would need different widgets to define the domain for a user reference vs. a node reference. Each widget would indicate what data type (or perhaps data types) it needed in a call back.
I think this solution is weaker because it suffers from much of the same issue CCK currently faces. Namely, large amounts of duplication of code. It has a slight improvement because one does not need to define both a new field and a new widget, one only needs to define a new widget.
I am new to CCK, so please feel free to explain why I am wrong in this matter. I think CCK is the future of Drupal (both from a user and from a module developer point of view), and I would like to see the best design decisions be incorporated early on.
Thank you,
-Mark
Comments
Comment #1
dopry commentedfield is data layer storage and validation, a widget is input UI, validation, and preparation for the field/data layer... This is now documented in the handbook at http://drupal.org/node/82661
Comment #2
(not verified) commented