ContactForm7でselectにoptgroupをフックで追加したい

いろいろ調べたけどjavascript使う方法は避けたい。
できればスマートにフックしたかったけど、wpcf7側の書き方的に無理っぽい。

海外ではプラグインのコードを上書きしちゃう方法を見つけたけど、そうすると
更新時にいちいち元に戻るので、それも避けたい。
っていうか生理的にプラグインのコードを上書きしたくない。

そこでこんな方法でフックしてみた。

ちょっとコードは汚いままだけど、都道府県の実装もついでに載せておく。

selectでoptgroupを使えるようにする

step1. wpcf7で設定されているselectのタグ書き出し時に呼ばれるフックを外す

functions.php
[php]
remove_action(‘wpcf7_init’, ‘wpcf7_add_shortcode_select’);
[/php]

step2. そして外してしまったwpcf7_add_shortcode_selectの代わりに自分の関数が呼ばれるようにする

その方法はselect.phpからコピペするだけ

functions.php
[php]
add_action(‘wpcf7_init’, ‘my_wpcf7_add_shortcode_select’);

function my_wpcf7_add_shortcode_select()
{
wpcf7_add_shortcode(array(‘select’, ‘select*’),
‘my_wpcf7_select_shortcode_handler’, true);
}
[/php]

step3. optgroupが使えるようにする

この海外の記事を参考に。
ちょっと長いけど全部のせておく。

functions.php
[php]

function my_wpcf7_select_shortcode_handler($tag)
{
$tag = new WPCF7_Shortcode($tag);

if (empty($tag->name))
    return '';

$validation_error = wpcf7_get_validation_error($tag->name);

$class = wpcf7_form_controls_class($tag->type);

if ($validation_error)
    $class .= ' wpcf7-not-valid';

$atts = array();

$atts['class'] = $tag->get_class_option($class);
$atts['id'] = $tag->get_id_option();
$atts['tabindex'] = $tag->get_option('tabindex', 'int', true);

if ($tag->is_required())
    $atts['aria-required'] = 'true';

$atts['aria-invalid'] = $validation_error ? 'true' : 'false';

$multiple = $tag->has_option('multiple');
$include_blank = $tag->has_option('include_blank');
$first_as_label = $tag->has_option('first_as_label');

$values = $tag->values;
$labels = $tag->labels;

if ($data = (array)$tag->get_data_option()) {
    $values = array_merge($values, array_values($data));
    $labels = array_merge($labels, array_values($data));
}

$defaults = array();

$default_choice = $tag->get_default_option(null, 'multiple=1');

foreach ($default_choice as $value) {
    $key = array_search($value, $values, true);

    if (false !== $key) {
        $defaults[] = (int)$key + 1;
    }
}

if ($matches = $tag->get_first_match_option('/^default:([0-9_]+)$/')) {
    $defaults = array_merge($defaults, explode('_', $matches[1]));
}

$defaults = array_unique($defaults);

$shifted = false;

if ($include_blank || empty($values)) {
    array_unshift($labels, '---');
    array_unshift($values, '');
    $shifted = true;
} elseif ($first_as_label) {
    $values[0] = '';
}

$html = '';
$hangover = wpcf7_get_hangover($tag->name);

foreach ($values as $key => $value) {
    $selected = false;

    if ($hangover) {
        if ($multiple) {
            $selected = in_array(esc_sql($value), (array)$hangover);
        } else {
            $selected = ($hangover == esc_sql($value));
        }
    } else {
        if (!$shifted && in_array((int)$key + 1, (array)$defaults)) {
            $selected = true;
        } elseif ($shifted && in_array((int)$key, (array)$defaults)) {
            $selected = true;
        }
    }

    $item_atts = array(
        'value' => $value,
        'selected' => $selected ? 'selected' : '');

    $item_atts = wpcf7_format_atts($item_atts);

    $label = isset($labels[$key]) ? $labels[$key] : $value;

    /**********************************
     *
     * 以下OPTGROUP用に改造した箇所
     *
     **********************************/

// $html .= sprintf( ‘‘,
// $item_atts, esc_html( $label ) );

    //BOF AMMENDMENT
    if (strpos(esc_attr($value), "!optgroup") > -1) {
        $exploded_value = explode('-', esc_attr($value));

        $html .= "<optgroup label=\"" . $exploded_value[1] . "\">";
    } elseif (esc_attr($value) == "optgroup!") {
        $html .= "</optgroup>";
    } //EOF AMMENDMENT
    else {
        $html .= sprintf('<option %1$s>%2$s</option>',
            $item_atts, esc_html($label));
    }
    //ここまで
}

if ($multiple)
    $atts['multiple'] = 'multiple';

$atts['name'] = $tag->name . ($multiple ? '[]' : '');

$atts = wpcf7_format_atts($atts);

$html = sprintf(
    '<span class="wpcf7-form-control-wrap %1$s"><select %2$s>%3$s</select>%4$s</span>',
    sanitize_html_class($tag->name), $atts, $html, $validation_error);

return $html;

}
[/php]

使い方

使い方は、以下のように。

wordpressのcf7管理画面
[php]
[select* prefecture ‘!optgroup-関東’ ‘東京都’ ‘神奈川県’ ‘optgroup!’ ‘!optgroup-近畿’ ‘大阪’ ‘京都’ ‘optgroup!’]
[/php]

都道府県のselectをoptgroup付きで出力してみる

上述したようにダラダラと都道府県を書いていけばいいのだが、管理画面使いづらいし、フォームの他のコードも見づらくなるので、これもフックで処理したい。

ショートタグ

まずwordpressのcf7管理画面で以下のようにシンプルに書いておく。

wordpressのcf7管理画面
[php]
[select* prefecture]
[/php]

フック

つぎにフックする。

functions.php
[php]
function wpcf7_prefecture_form_tag($tag)
{

if (!is_array($tag)) {
    return $tag;
}

$tagName = $tag['name'];
$basetype = $tag['basetype'];

if ('prefecture' == $tagName && 'select' == $basetype) {
    $regions = [
        '関東地方' => [
            '東京都', '神奈川県', '埼玉県', '千葉県', '茨城県', '栃木県', '群馬県'
        ],
        '北海道・東北地方' => [
            '北海道', '青森県', '岩手県', '宮城県', '秋田県', '山形県', '福島県'
        ],
        '中部地方' => [
            '新潟県', '富山県', '石川県', '福井県', '山梨県', '長野県', '岐阜県', '静岡県', '愛知県'
        ],
        '近畿地方' => [
            '大阪府', '兵庫県', '三重県', '滋賀県', '京都府', '奈良県', '和歌山県'
        ],
        '中国地方' => [
            '鳥取県', '島根県', '岡山県', '広島県', '山口県'
        ],
        '四国地方' => [
            '徳島県', '香川県', '愛媛県', '高知県'
        ],
        '九州・沖縄地方' => [
            '福岡県', '佐賀県', '長崎県', '熊本県', '大分県', '宮崎県', '鹿児島県', '沖縄県'
        ]];

    $results = [];

    foreach ($regions as $region => $prefectures) {
        $results[] = sprintf('!optgroup-%s', $region);

        foreach ($prefectures as $prefecture) {
            $results[] = $prefecture;
        }

        $results[] = 'optgroup!';
    }

    $tag['values'] = $results;
}

return $tag;

}
add_filter(‘wpcf7_form_tag’, ‘wpcf7_prefecture_form_tag’, 11);
[/php]