5

I am using a multi value image field which is set to hold 5 values. What I need, is to be able to set a lower cardinality for this field on specific nodes of the same content type.

Edit: Below is what I ended up using, it might help someone.

/**
 * Implements hook_field_attach_form().
 */
function example_field_attach_form($entity_type, $entity, &$form, &$form_state, $langcode) {
  // Our field name
  $field_name = 'field_listing_images';

  // You may want to add more conditions
  if (isset($form[$field_name][LANGUAGE_NONE])) {

    // Our custom cardinality (hardcoded here)
    $custom_cardinality = 3;

    // Unset all field multi values that are equal or larger than our $custom_cardinality variable
    foreach ($form[$field_name][LANGUAGE_NONE] as $k => $value) {
      if ($k >= $custom_cardinality) {
        unset($form[$field_name][LANGUAGE_NONE][$k]);
      }
    }

    // Our custom form validation
    $form['#validate'][] = 'example_custom_form_validate';
  }
}
/**
 * Our custom form validation callback.
 */
function example_custom_form_validate($form, &$form_state) {
  // Our field name
  $field_name = 'field_listing_images';

  // You may want to add more conditions
  if (isset($form_state['values'][$field_name][LANGUAGE_NONE])) {

    // Our custom cardinality (hardcoded here)
    $custom_cardinality = 3;

    // Unset all field multi values that are equal or larger than our $custom_cardinality variable
    foreach ($form_state['values'][$field_name][LANGUAGE_NONE] as $k => $value) {
      if ($k >= $custom_cardinality) {
        unset($form_state['values'][$field_name][LANGUAGE_NONE][$k]);
      }
    }
  }
}

4 Answers 4

0

After some digging around in the field API, I can only conclude that I cannot find a way to do this properly. If the cardinality is available in a hook prior to rendering, then I cannot find it. If you want to attempt to research more yourself, I recommend the Field Attach API. What I did find, was hook_field_attach_form, in which you can do:

function hook_field_attach_form($entity_type, $entity, &$form, &$form_state, $langcode) {
  $form['field_test'][LANGUAGE_NONE][4]['#access'] = FALSE;
}

to deny access to the last element. Unfortunately, this has the "nice" effect of leaving an empty drag handle element on the page. In order to get rid of that, you need to

function hook_field_attach_form($entity_type, $entity, &$form, &$form_state, $langcode) {
  $form['field_test'][LANGUAGE_NONE][4]['#access'] = FALSE;
  unset($form['field_test'][LANGUAGE_NONE][4]);
}

but unset-ting form elements like that is just plain wrong, and can potentially cause issues with form validation.

What you may want to do instead, is move the whole logic in a custom validation form, and simply reject form submission if to many values are entered. See this stackexchange question for custom node validation, it's relevant to D7 as well.

1
  • It looks like the cardinality is not available in any hooks and is something that might be added in Drupal 8 node/1029298. I tried your solution and it works fine for what I want to achieve. Thanks!
    – ovi
    Jan 7, 2012 at 21:12
1

My cardinality was based on the value of another field in a related entity, so I figured out that I could set the number of values to Unlimited in the field settings, then, in hook_form_alter, reattach the field to the form with a new cardinality:

$field = field_info_field('field_my_field');
$field['cardinality'] = $my_new_cardinality;
$instance = field_info_instance('entity_type', 'field_my_field', 'bundle');

// Depending on the form, the entity may be found in $form['#entity'],
// in $form_state, or elsewhere.
// $langcode is usually LANGUAGE_NONE, but may change in multilingual sites.
$items = isset($entity->field_my_field[$langcode]) ? $entity->field_my_field[$langcode] : array();
$new_field = field_multiple_value_form($field, $instance, $langcode, $items, $form, $form_state);

// This is not needed. It tricks theme_field_multiple_value_form into
// not adding the value reordering widget.
$new_field['#cardinality'] = 0;
foreach (element_children($new_field) as $delta) {
  $new_field[$delta]['_weight']['#access'] = FALSE;
}

$form['field_my_field'][$langcode] = $new_field;
0

The right way to do this is to help get this patch in and use hook_field_widget_properties_alter() or module "Field instance cardinality".

0

Currently it's not possible to restrict cardinality in the field instance settings, but you can use the patch from there.

As workaround you may set the the maximum cardinality for a multi value field by modifying items_count and its cardinality in its $field_state (see: field_form_get_state), then do extra checking in hook_form_alter, e.g.

$limit = 3 // Your dynamic limit.
$field_state = field_form_get_state($parents, $field_name, $langcode, $form_state)
$field_state['items_count'] = $limit;
$field_state['field']['cardinality'] = $limit;

The above code can be triggered e.g. from rules, see: How to change field cardinality per form either programatically or by rule? for complete example.

Then in hook_form_alter() you may check if items_count reached its cardinality, then disable Add and Remove buttons. For example:

$field = &$form['FIELDNAME'];
$field_state = field_form_get_state($field[$field['#language']]['#field_parents'], $field[$field['#language']]['#field_name'], $langcode, $form_state);
if ($field_state['items_count'] == $field_state['field']['cardinality']) {
  $field[$langcode]['add_more']['#access'] = FALSE;
  foreach (array_column($field[$langcode], 'remove_button') as $key2 => $rm_item) {
    $field[$langcode][$key2]['remove_button']['#access'] = FALSE;
  }
}

Alternatively try the following module:

which enhances settings for muliple value fields.

multivalue_extras_field_settings - Multivalue Extras Drupal module

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.