How to change field cardinality per form either programatically or by rule?
There are the following hooks which allow to alter the widget form such as hook_field_widget_form_alter and hook_field_widget_WIDGET_TYPE_form_alter, but they only apply to widget form element.

As for workaround, it's possible to set the field to Unlimited and use hook_form_alter to remove add_more element manually like:

$form[$field_name][LANGUAGE_NONE][0]['remove_button']['#access'] = FALSE;
$form[$field_name][LANGUAGE_NONE]['add_more']['#access'] = FALSE;


or overriding/removing #theme:

unset($form['field_foo'][LANGUAGE_NONE][0]['#theme']);


for certain conditions.



Using Rules, it's possible to use Rules Forms Support module which can deal with form elements, so you can change the items_count. Here is sample rule dealing with a multi value field in Entityform:

/**
 * Implements hook_action_info().
 */
function mymodule_entityform_rules_rules_action_info() {
  return array(
    'foo_entityform_rules_set_cardinality_value' =&gt; array(
      'label' =&gt; t('Change items count of a multi value field'),
      'group' =&gt; t('Rules Forms'),
      'parameter' =&gt; array(
        'form' =&gt; array('type' =&gt; 'form', 'label' =&gt; t('Form')),
        'form_state' =&gt; array('type' =&gt; 'form_state', 'label' =&gt; t('Form State')),
        'element' =&gt; array('type' =&gt; 'form_element', 'label' =&gt; t('Target Element')),
        'value' =&gt; array('type' =&gt; 'form_element', 'label' =&gt; t('Value from Element')),
      ),
      'base' =&gt; 'foo_entityform_rules_set_cardinality_value',
      'callbacks' =&gt; array(
        'access' =&gt; 'rules_forms_integration_access',
        'form_alter' =&gt; 'rules_forms_element_form_alter',
      ),
    ),
  );
}

/**
 * Implementation of rule callback.
 */
function mymodule_entityform_rules_set_cardinality_value($form, $form_state, $element, $value) {
  module_load_include("module", "rules_forms", 'rules_forms');
  $val_elem = &amp;_rules_forms_get_element($form, $value);
  if (!isset($form_state['field'])) {
    return;
  }

  $element = substr($element, strpos($element, ':') + 1);
  $path = explode(':', $element);
  if (count($path) == 1) {
    $path[] = LANGUAGE_NONE;
  }

  $field_state = &amp;drupal_array_get_nested_value($form_state['field'], $path);
  if (!$field_state) {
    return;
  }

  $limit = !empty($val_elem['#value']) ? $val_elem['#value'] : (!empty($val_elem['#default_value']) ? $val_elem['#default_value'] : NULL);
  $field_state['items_count'] = $limit;
  $field_state['field']['cardinality'] = $limit; // Won't work for field_multiple_value_form().
}


This works when field is set to Unlimited, however Add another and Remove buttons still needs to be removed from form_alter as shown below:

/**
 * Implements hook_form_alter().
 */
function mymodule_form_alter(&amp;$form, &amp;$form_state, $form_id) {

    // Check cardinality.
    // If items_count for a field has reach its cardinality limit,
    // then remove Add more and Remove buttons.
    foreach ($form as $key =&gt; $item) {
      if (is_array($item) &amp;&amp; !empty($item['#type']) &amp;&amp; !empty($item['#language']) &amp;&amp; $item['#type'] == 'container') {
        $field = &amp;$form[$key];
        $langcode = $field['#language'] ?: LANGUAGE_NONE;
        $field_state = field_form_get_state($field[$langcode]['#field_parents'], $field[$langcode]['#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 =&gt; $rm_item) {
            $field[$langcode][$key2]['remove_button']['#access'] = FALSE;
          }
        }
      }
    } // end: foreach
  }
}




It's also worth to try some patches from Allow cardinality to be restricted (overridden and reduced) in the field instance settings or  Add hook alter to override field_multiple_value_form().

There is also Field instance cardinality module which allows the cardinality of specific instances of fields to be overridden.

Some other proposed workaround from here by @donquixote (without actually hacking Drupal core) could include:


a new widget type with FIELD_BEHAVIOR_CUSTOM with widget settings to change a cardinality limit, the name of another ("decorated") field widget type and other settings for the decorated field widget type,
add a number field in the instance settings form and use hook_field_widget_properties_alter to replace $instance['widget']['module'] (keep original in $instance['widget']['_module']) and replace override $instance['widget']['type'] with own FIELD_BEHAVIOR_CUSTOM and save the original in $instance['widget']['_type'], so you can do the same decorator trick in hook_field_widget_form as above.




Modules

Related modules:


Multivalue Extras


  Enhances settings for multiple value fields.