Emulate the CCK form validation process for a given field

Share and options

Something that you may need to do one day or another is validating a custom form field value as if it was a Content field existing value, outside of the node form context.

There is something quite sad about Content module, that is the API does not seems (in Drupal 6, at least, I'm awaiting for a lot of improvements in D7 Field API) to expose any function for manual field validation. The whole process of validation goes through the numerous iterations in node_form and in hook_nodeapi() content module's implementation.

I found a way, quite ugly, but tested and working, let's analyse the following code (comments are inside the function)...

<?php
/**
* Validate single value for the given CCK field.
*
* FIXME: This method is ugly, but will work. It could have some visible
* impact on performances.
*
* @param object $node
*   The current node on which we are trying to validate the value.
*   Here see that we must have a node, this is because of the content module's
*   functions signature we are going to use. I think this could go well with a fake
*   node object (need further testing though).
* @param string|array $field
*   Field name or loaded field description.
* @param array $item
*   Single value as it would be spawned in the node->field_* array.
*
* @return boolean
*/
function _bricks_field_validate_value($node, $field, $item) {
 
// Fetch field description.
 
if (!is_array($field)) {
   
$field = content_fields($field);
  }

  // Prepare the specific hook_field() function.
 
$func = $field['module'] . '_field';
  if (
function_exists($func)) {

    // First, if we are in a form, we must backup the current form errors
    // array in order to set it back later.
   
$errors = form_get_errors();

    // Because we are going to simulate a form, empty the current form
    // errors array.
   
form_set_error(NULL, '', TRUE);

    // Prepare a false items array, such as the content module would give
    // to this exact same hook.
   
$items = array(0 => $item);

    // Execute the current hook, which will fill the current forms errors
    // if validation does not pass.
   
$func('validate', $node, $field, $items, NULL, NULL);

    // Check we have errors.
   
$ret = ! (bool) form_get_errors();

    // Set back the form errors to its original state.
   
form_set_error(NULL, '', TRUE);
    if (!empty(
$errors)) {
      foreach (
$errors as $name => &$message) {
       
form_set_error($name, $message);
      }
    }

    // Finally, return the result.
   
return $ret;
  }
}
?>

As you can see, the whole process is a Form API hack that backup the current form context, if any, in order to execute content module's specific validation hook, that is meant to work only in a form context. We backup the current form errors state in order to set it back later, so if we execute this function in a form context, we won't break it!

If anyone has a better method, please, I'm begging you, post it :)