0

I've custom widget which consist multiple input elements in hook_field_widget_form() as part of one field (like address field).

The problem is that Drupal complains about:

this field cannot hold more than @count values

which happens in field_default_validate(), when I've multiple input filled, so it doesn't recognise that this is one single field.

I've tried to change my cardinality in every possible place as below, but without success:

/** 
 * Implements hook_field_info().
 */ 
function my_address_field_field_info() {
  return [
    'my_address_field' => [
      'label' => t('My Address Field'),
      'settings' => array('cardinality' => FIELD_CARDINALITY_UNLIMITED),
      'instance_settings' => array('cardinality' => FIELD_CARDINALITY_UNLIMITED),
      'default_widget' => 'my_address_field_widget',
      'default_formatter' => 'my_address_field_default',
      'cardinality' => FIELD_CARDINALITY_UNLIMITED,
    ],
  ];    
}

/**
 * Implements hook_field_widget_info().
 */
function my_address_field_field_widget_info() {
  return [
    'my_address_field_widget' => [
      'label' => t('Default'),
      'field types' => ['my_address_field'],
      'settings' => array(
        'size' => 60,
        'cardinality' => FIELD_CARDINALITY_UNLIMITED,
      ),
      'behaviors' => array(
        'multiple values' => FIELD_BEHAVIOR_NONE,
        'default value' => FIELD_BEHAVIOR_NONE,
      ),
      'cardinality' => FIELD_CARDINALITY_UNLIMITED,
    ],
  ];  
}

/**
 * Implements hook_field_widget_properties_alter().
 */
function my_address_field_field_widget_properties_alter(&$widget, $context) {
  $widget['cardinality'] = FIELD_CARDINALITY_UNLIMITED;
}

/**
 * Implements hook_field_formatter_info().
 */
function my_address_field_field_formatter_info() {
  return [
    'my_address_field_default' => [
      'label' => t('Default'),
      'field types' => ['my_address_field'],
      'multiple values' => FIELD_BEHAVIOR_DEFAULT,
    ],
  ];
}

/**
 * Implements hook_field_widget_form().
 */
function my_address_field_field_widget_form(&$form, &$form_state, $field, $instance, $langcode, $items, $delta, $element) {

  $widget = array();
  $widget['label'] = array(
    '#title' => $instance['label'],
    '#theme' => 'form_element_label',
  );
  $widget['address_line_1'] = array(
    '#type' => 'textfield',
    '#title' => t('Address line 1'),
  );
  $widget['address_line_2'] = array(
    '#type' => 'textfield',
    '#title' => t('Address line 2'),
  );
  // more text fields here ...

  return $widget;
}

Including defining hook_field_validate which I thought it'll override default field validation handler, but this was not the case.

What's the right way of assigning unlimited cardinality for my widget?

3
  • The problem might be in hook_field_widget_form() (you might need to go one level deeper in the array you return), could you add your code for that function?
    – Clive
    Commented Nov 26, 2015 at 18:36
  • @Clive I've added my implementation of hook_field_widget_form. Possibly that I'm implementing multi-input field in the wrong way, and it needs to be rewritten as Drupal treats them as separate elements. So as simple workaround I wanted to disable the validation, but it seems it's not possible.
    – kenorb
    Commented Nov 26, 2015 at 19:39
  • It's hard to say what's wrong just by looking at the code. How can I reproduce this? Do you also have a schema? By the way, have you looked at the multifield module? Commented Dec 2, 2015 at 13:43

2 Answers 2

1

You can use Field instance cardinality module which allows the cardinality of specific instances of fields to be overridden.

This module allows the cardinality of specific instances of fields to be overridden.

This currently only works with fields that use options widgets (ie select list or checkboxes), and only allows them to be overridden to be single-valued.

1
  • Hello. Could you add excerpt from module page?
    – Mołot
    Commented Dec 17, 2015 at 9:04
1

Since the widget form (hook_field_widget_form) returns multiple elements at once, by default Drupal recognise them as multiple values of one widget. To override that behaviour, you need to define your properties in property_callbacks hook as part of your hook_field_info which can return value of your property (in the format documented for your data type). See: Entity property information.

Then make sure corresponding properties have their Field API schema for a field structure by implementing hook_field_schema in your .install file. Also don't forget to define hook_field_is_empty which tell what constitutes an empty item for a field type.

Here is example code:

/**
 * Implements hook_field_schema().
 *
 * This should be placed in .install file.
 */
function MYMODULE_field_schema($field) {
  $column_names = array(
    'addressline1',
    'addressline2',
  );
  $columns = array();
  foreach ($column_names as $column_name) {
    $columns[$column_name] = array(
      'type' => 'varchar',
      'length' => 255,
      'not null' => FALSE,
    );
  }
  return array(
    'columns' => $columns,
  );
}

/**
 * Implements hook_field_info().
 */
function MYMODULE_field_info() {
  return array(
    'MYFIELD' => array(
      'label' => t('My Field'),
      'description' => t('Provides a custom field.'),
      'settings' => array(),
      'instance_settings' => array(),
      'property_type' => 'MYFIELD',
      'property_callbacks' => array('MYMODULE_property_info_callback'),
    ),
  );
}

/**
 * Callback to alter the property info of custom field.
 */
function MYMODULE_property_info_callback(&$info, $entity_type, $field, $instance, $field_type) {
  $name = $field['field_name'];
  $property = &$info[$entity_type]['bundles'][$instance['bundle']]['properties'][$name];

  $property['type'] = 'MYFIELD';
  $property['getter callback'] = 'entity_metadata_field_verbatim_get';
  $property['setter callback'] = 'entity_metadata_field_verbatim_set';

  unset($property['query callback']);
}

/**
 * Implements hook_field_is_empty().
 */
function MYMODULE_field_is_empty($item, $field) {

  // Check if required address components are empty.
  foreach (array('addressline1', 'addressline2') as $required_component) {
    if (empty($item[$required_component])) {
      return TRUE;
    }
  }

  return FALSE;
}

For complete example, check how Address Field module implements its field properties, so they work correctly with cardinality settings.

See also: How to write a property callback for a custom field

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.