web-dev-qa-db-ja.com

フィールドのカーディナリティを変更するには、プログラムまたはルールのどちらで実行しますか?

複数ページのフォーム(各ページはフィールドグループによって決定されます)があります。最初のページのユーザータイプはテキストフィールドの人数で、2番目のフォームは指定された数に基づいてX個のフィールドコレクションインスタンスを表示します。 。したがって、ユーザーが5人を選択した場合、ユーザーのために入力する5つのフィールドコレクションプレースホルダーが表示され、それ以上は表示されないことを期待します別の項目を追加ボタン(特にフィールドのカーディナリティが無制限に設定されている場合)。

ルールまたはコードからフォームの状態ごとにカーディナリティを設定できる解決策はありますか?

3
kenorb

hook_field_widget_form_alterhook_field_widget_WIDGET_TYPE_form_alter などのウィジェットフォームを変更できる次のフックがありますが、それらはウィジェットフォーム要素にのみ適用されます。

回避策として、フィールドを無制限に設定し、 hook_form_alter を使用して、次のように手動でadd_more要素を削除することができます。

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

または#themeのオーバーライド/削除:

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

特定の条件の場合。


ルールを使用すると、フォーム要素を処理できる Rules Forms Support モジュールを使用できるため、items_countを変更できます。 Entityform の複数値フィールドを処理するサンプルルールを次に示します。

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

これは、フィールドが無制限に設定されている場合に機能しますが、追加および削除以下に示すように、ボタンはform_alterから削除する必要があります。

/**
 * Implements hook_form_alter().
 */
function mymodule_form_alter(&$form, &$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 => $item) {
      if (is_array($item) && !empty($item['#type']) && !empty($item['#language']) && $item['#type'] == 'container') {
        $field = &$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 => $rm_item) {
            $field[$langcode][$key2]['remove_button']['#access'] = FALSE;
          }
        }
      }
    } // end: foreach
  }
}

フィールドインスタンスの設定でカーディナリティを制限(オーバーライドおよび削減)できるようにする または field_multiple_value_form() をオーバーライドするフック変更を追加することも試してみる価値があります。

また、フィールドの特定のインスタンスのカーディナリティをオーバーライドできるようにする Field instance cardinality モジュールもあります。

here による @ donquixote (実際にハッキングすることなくDrupal core)から)が提案する他の回避策には、次のものが含まれます。

  • FIELD_BEHAVIOR_CUSTOMを使用した新しいウィジェットタイプ カーディナリティ制限を変更するウィジェット設定、別の(「装飾」された)フィールドウィジェットタイプの名前、および装飾されたフィールドウィジェットタイプの他の設定、
  • インスタンス設定フォームに数値フィールドを追加 し、hook_field_widget_properties_alterを使用して$instance['widget']['module']を置き換え($instance['widget']['_module']で元を保持)、override $instance['widget']['type']を独自のFIELD_BEHAVIOR_CUSTOMで置き換え、元の$instance['widget']['_type']に保存するので、同じデコレータトリックを上記のhook_field_widget_form

モジュール

関連モジュール:

6
kenorb

@ BDuell が提案する別のアプローチ post

  // In your hook_form_FORM_ID_alter() function...

  // Our field and the number of values that we need to have in it...
  $field_name = 'field_machine_name';
  $items_count = count($array_of_values);

  // If we have an array of values that need to be prepopulated...
  if (!empty($array_of_values) && $form_state['field'][$field_name][LANGUAGE_NONE]['items_count']!=$items_count) {
    $form_state['field'][$field_name][LANGUAGE_NONE]['items_count'] = $items_count;
    $form_theme = $form[$field_name][LANGUAGE_NONE]['#theme'];
    $items = &$form_state['field'][$field_name][LANGUAGE_NONE];
    // Generate required number of fields collection
    if ($items_count > 1 and $items['items_count'] != $items_count) {
      $items['items_count'] = $items_count;
      $items['field']['cardinality'] = $items_count;
      $form[$field_name][LANGUAGE_NONE] = field_multiple_value_form($items['field'], $items['instance'], LANGUAGE_NONE, array(), $form, $form_state);
      // Reset theme function, as field_multiple_value_form hijacks it
      $form[$field_name][LANGUAGE_NONE]['#theme'] = $form_theme;
    }

    // Tell our form that we need to rebuild once we are done...
    $rebuild_p = TRUE;
  }

  // ANY OTHER FORM ALTER STUFF

  // If we need to rebuild, notify the form...
  if ($rebuild_p) $form['#after_build'][] = 'MYMODULE_after_build';

次に、カスタムモジュールで次のようにhook_after_buildを定義できます。

/**
 * Rebuild the form if necessary...
 */
function MYMODULE_after_build($form, &$form_state) {
  return drupal_rebuild_form($form_state['build_info']['form_id'], $form_state, $form);
}
0
kenorb