[WordPress]Contact Form 7 に独自タグでチェックボックスを追加【特定カテゴリー編】


[前回の記事](https://qiita.com/noqua/items/42522f12ccbf65238ea1)で[Contact Form 7](https://ja.wordpress.org/plugins/contact-form-7/)に独自チェックボックスを追加する方法を書きました。
前回は **固定ページ** を動的にチェックボックス化しましたが、同じ要領で今回は **特定カテゴリー記事** をチェックボックス化するコードの紹介になります。

## 仕様

- Contact Form 7の設定画面で独自タグ ```[cat_list]``` を利用
	<img src="https://qiita-image-store.s3.amazonaws.com/0/62022/8ea44a77-efbd-9875-b1ae-20a9f58ae9d3.png" alt="独自タグ使用イメージ" width="865">

- 特定カテゴリーに属する記事のタイトルがチェックボックスとなる
	<img src="https://qiita-image-store.s3.amazonaws.com/0/62022/83d027b7-6e46-5609-f1df-9f6ec548ac06.png" alt="カテゴリー選択イメージ" width="977">

	<img src="https://qiita-image-store.s3.amazonaws.com/0/62022/62b69936-ea02-fe23-27b9-5fd4107fa63d.png" alt="フォーム表示イメージ" width="597">



- Contact Form 7標準のチェックボックス同様、以下のオプションも選択できる
	- 必須項目
	- 名前
	- ラベルを前に、チェックボックスを後に配置する
	- 個々の項目を label 要素で囲む
	- チェックボックスを排他化する
	- ID属性
	- クラス属性
	<img src="https://camo.qiitausercontent.com/533846ac0b6e3fcbfcaeae1aa1c38a65230dcedb/68747470733a2f2f71696974612d696d6167652d73746f72652e73332e616d617a6f6e6177732e636f6d2f302f36323032322f33636639376230382d616461322d306266322d316165612d3362323035303864346430662e706e67" alt="チェックボックスのオプションイメージ" data-canonical-src="https://qiita-image-store.s3.amazonaws.com/0/62022/3cf97b08-ada2-0bf2-1aea-3b20508d4d0f.png">

## 検証バージョン

- WordPress : 4.9.4
- Contact Form 7 : 5.0.1

## 実装

[前回](https://qiita.com/noqua/items/42522f12ccbf65238ea1#%E5%AE%9F%E8%A3%85)同様、 ```function.php``` に関数を書いていきます。

### function.php

```php:function.php
// Contact Form 7 に独自チェックリストを追加
add_action( 'wpcf7_init', 'wpcf7_add_form_tag_catlist' );

// 独自タグ[cat_list]を定義（末尾*は必須項目用）
function wpcf7_add_form_tag_catlist() {
	wpcf7_add_form_tag( array( 'cat_list', 'cat_list*' ),
		'wpcf7_pagelist_form_tag_handler', // 下で設定する関数名が入る
		array(
			'name-attr' => true,
			'selectable-values' => true,
			'multiple-controls-container' => true,
		)
	);
}

// 独自タグの内容を定義する関数
function wpcf7_pagelist_form_tag_handler( $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';
	}

	// オプションの設定
	$label_first		= $tag->has_option( 'label_first' );
	$use_label_element	= $tag->has_option( 'use_label_element' );
	$exclusive			= $tag->has_option( 'exclusive' );
	$multiple			= false;

	if ( 'cat_list' == $tag->basetype ) {
		$multiple = ! $exclusive;
	}

	if ( $exclusive ) {
		$class .= ' wpcf7-exclusive-checkbox';
	}

	$atts = array();

	$atts['class']	= $tag->get_class_option( $class );
	$atts['id']		= $tag->get_id_option();

	$tabindex = $tag->get_option( 'tabindex', 'signed_int', true );

	if ( false !== $tabindex ) {
		$tabindex = (int) $tabindex;
	}

	// カテゴリーを指定するプロパティ
	// cat_id:xxxx でIDから指定
	$cat_id = ''; // default
	$cat_id = ( $cat_id = $tag->get_option('cat_id', '', true ))? esc_attr($cat_id) : '';

	// cat_slug:xxxx でスラッグから指定
	$cat_slug = ( $cat_slug = $tag->get_option('cat_slug', '', true ))? esc_attr($cat_slug) : '';
	if($cat_slug) {
		$category = get_category_by_slug($cat_slug);
		$cat_id	= $category -> term_id;
	}

	// カテゴリーの記事を取得
	$cat_posts = 'numberposts=-1&category='.$cat_id.'&order=ASC&orderby=menu_order';
	$posts = get_posts($cat_posts); global $post;

	$html = '';
	$count = 0;

	$hangover = wpcf7_get_hangover( $tag->name, $multiple ? array() : '' );

	// 表示の設定
	foreach($posts as $post) {
		$class = 'wpcf7-list-item';

		$checked 	= false;
		$key		= $count;
		$value		= get_the_title();
		$label		= get_the_title();

		if ( $hangover ) {
			if ( $multiple ) {
				$checked = in_array( $value, (array) $hangover, true );
			} else {
				$checked = ( $hangover === $value );
			}
		} else {
			$checked = in_array( $count + 1, (array) $defaults );
		}

		$item_atts = array(
			'type'		=> 'checkbox',
			'name'		=> $tag -> name . ( $multiple ? '[]' : '' ),
			'value'		=> $value,
			'checked'	=> $checked ? 'checked' : '',
			'tabindex'	=> false !== $tabindex ? $tabindex : '',
		);

		$item_atts = wpcf7_format_atts( $item_atts );

		if ( $label_first ) { // put label first, input last
			$item = sprintf(
				'<span class="wpcf7-list-item-label">%1$s</span><input %2$s />',
				esc_html( $label ), $item_atts );
		} else {
			$item = sprintf(
				'<input %2$s /><span class="wpcf7-list-item-label">%1$s</span>',
				esc_html( $label ), $item_atts );
		}

		if ( $use_label_element ) {
			$item = '<label>' . $item . '</label>';
		}

		if ( false !== $tabindex && 0 < $tabindex ) {
			$tabindex += 1;
		}

		$count += 1;

		if ( 1 == $count ) {
			$class .= ' first';
		}

		if ( count( $children ) == $count ) { // last round
			$class .= ' last';
		}

		$item = '<span class="' . esc_attr( $class ) . '">' . $item . '</span>';
		$html .= $item;
	} // foreach

	$atts = wpcf7_format_atts( $atts );

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

	// postをリセット
	wp_reset_postdata();

	return $html;
}

// バリデートの設定
add_filter( 'wpcf7_validate_cat_list', 'wpcf7_cat_list_validation_filter', 10, 2 );
add_filter( 'wpcf7_validate_cat_list*', 'wpcf7_cat_list_validation_filter', 10, 2 );

function wpcf7_cat_list_validation_filter( $result, $tag ) {
	$type = $tag->type;
	$name = $tag->name;
	$is_required = $tag->is_required() || 'cat_list*' == $type; // 'cat_list*' は独自タグ
	$value = isset( $_POST[$name] ) ? (array) $_POST[$name] : array();

	if ( $is_required && empty( $value ) ) {
		$result->invalidate( $tag, wpcf7_get_message( 'invalid_required' ) );
	}

	return $result;
}
```

### Contact Form 7設定画面

今回追加したチェックボックスを生成する独自タグは ```[cat_list]``` となります。

- 必須項目にする場合は末尾に　*　を加える [cat_list*]
- カテゴリーIDもしくはスラッグを指定 （※いずれか必須）
	- IDを指定する場合は ```cat_id:カテゴリーID```
	- スラッグで指定する場合は ```cat_slug:カテゴリースラッグ``` と書きます。※カテゴリースラッグは日本語だと動きません。

```php
[cat_list* cat_id:2] // IDで指定する場合
[cat_list* cat_slug:スラッグ] // スラッグで指定する場合
```
		
さらにContact Form 7の標準チェックボックス同様、以下のオプションが指定出来ます。
書き方は[Contact Form 7](https://contactform7.com/ja/tag-syntax/#form_tag)の仕様ページを参考にしてください。

- 名前（inputのname値）
- ラベルを前に、チェックボックスを後に配置する ```label_first```
- 個々の項目を label 要素で囲む ```use_label_element```
- チェックボックスを排他化する ```exclusive```
- ID属性 ```id:hoge```
- クラス属性 ```class:foo```

※チェックボックスの項目は タグでは指定出来ません。

独自タグの書き方例

```php
[cat_list* seminar use_label_element cat_id:2]
```

<img src="https://qiita-image-store.s3.amazonaws.com/0/62022/d87d53a2-1c3c-5c76-a6a1-f972118c0b82.png" alt="Contact Form 7の設定画面" width="865">

## フォームの表示イメージ

<img src="https://qiita-image-store.s3.amazonaws.com/0/62022/62b69936-ea02-fe23-27b9-5fd4107fa63d.png" alt="フォーム表示イメージ" width="865">

バリデーションも動きます。

セレクトボックスやテキストボックス等を独自タグにする方法はいくつか見つかるのですが、チェックボックスのように複数選択するもののカスタマイズ方法はあまり見かけないので、前回の固定ページに引き続きカテゴリー記事から追加する方法もまとめてみました。

以上になります。
参考になりましたら幸いです。

---

固定ページ（子ページ）をチェックボックス化する方法はこちら。

- [[WordPress]Contact Form 7に下層ページ一覧をチェックボックスとして動的に追加](https://qiita.com/noqua/items/42522f12ccbf65238ea1)


下記のページを参考にさせていただきました。ありがとうございます。

- [Contact Form 7 で独自のフォームタグ作成（今年から●年後までが選択肢になるセレクトボックス）](https://qiita.com/shin1kt/items/e47f641091890bbe212f)

- [WordPress ContactForm7 動的なオリジナルのフォームタグを作りたい。](http://chaika.hatenablog.com/entry/2017/07/31/090000)