work.log

エンジニアの備忘録的ブログ

TwentyTwelveでカテゴリ連動型のタグ検索フォームを作成する

投稿:2013-10-31 19:16  更新:

WordPress のテーマ TwentyTwelve のカスタマイズ記事です。

記事の絞り込み機能を強化すべく、カテゴリ連動型セレクトボックス付きの検索フォームを作ってましたが、何とか形にできたので実装方法を書いておきたいと思います。

今回はこのような「カテゴリ連動型の検索フォーム」ができましたのでまずはサンプルから。

スポンサーリンク

プラグイン版のダウンロード

とりあえず使ってみたいという人向けに、以前作成したプラグイン版のリンクを貼っておきます。

wp-taxonomy-search.zip

最近のバージョンで動作確認をしていないため動かなかったらごめんなさい。

プラグイン中の func-taxonomy-search.php というファイルにセキュリティ用の文字列を予め記述しているので、ご利用前にランダムな文字列に変更しておいてください。

<?php

function get_nonce_key() {
	/* 「my_nonce」の部分をランダムな英数字に変更する */
	$nonce_key = 'my_nonce';
	return $nonce_key;

}

また、デザインはスタイルシートで調整してください。

連動型検索フォームのデモ

[taxonomy-search]

※ テーマを変更した為デモは未稼働状態です…

カテゴリを選択すると、タグのボックスには存在するタグのみが表示されると思います。

途中までは、カテゴリ、タグ相互に連動するのを作ろうとしてましたが、結構面倒だしコードだけが長くなってブログネタには適さなくなりそうだったので、とりあえずはカテゴリ連動型のフォームという事で。

以下でサンプルコードを記載して、簡単に実装方法を書いていきます。

カテゴリ連動型の検索フォームを設置する

これまでに小分けに書いた関連記事のコードを元に機能を組み立てていきます。と言っても、作っている課程で処理自体に問題が見つかって結構変えています。

今回の実装は以下のファイルを弄ります。

  • テーマ内の functions.php へコード追加
  • テーマ内の js/ へ Ajax 用スクリプトを作成し配置

functions.php は関数をいくつか作成していくので事前のバッグアップをオススメします。

カテゴリに関連するタグ情報を取得するコード

まずは、連動部分の要のコードです。渡されたターム ID からタクソノミーを判別、別の関連タクソノミーの情報を取得するといった感じの処理になります。

function get_relation_taxonomy( $term_id = FALSE ) {

	$term_ids = array(
		'term'     => array(),
		'taxonomy' => FALSE
	);

	if ( $term_id === FALSE ) {
		return $term_ids;
	}

	// 選択された id がカテゴリかタグかを調べる
	// 存在しない id の場合は処理を終了
	$taxonomy = get_terms(
			array(
				'category',
				'post_tag'
			),
			array(
				'hide_empty' => TRUE,
				'include'    => $term_id
			)
		    );

	$taxonomy = $taxonomy[0]->taxonomy;

	if ( ! is_null( $taxonomy ) ) {

		// 上記で判定したタクソノミーが設定された記事リストを取得
		$args = array(
			'nopaging'            => 1,
			'ignore_sticky_posts' => 1,
			'order'               => ASC,
			'tax_query'           => array( 
				array(
					'taxonomy' => $taxonomy,
					'terms'    => $term_id,
					'field'    => 'id',
					'operator' => 'AND'
				 )
			)
		);

		$my_query = new WP_Query( $args );

		if( $my_query->have_posts() ) {

			// タクソノミーを反転
			if ( $taxonomy === 'category' ) {
				$taxonomy = 'post_tag';
			} else {
				$taxonomy = 'category';
			}

			while ( $my_query->have_posts() ) : $my_query->the_post();

				$term = get_the_terms( get_the_ID(), $taxonomy );

				if ( $term ) {

					foreach ( $term as $value ) {

						if ( ! isset( $term_ids['term'][$value->term_id] ) ) {

							// 以下の情報を連想配列に格納
							$term_info = array(
								'id'    => $value->term_id,
								'name'  => $value->name,
								'slug'  => $value->slug,
							);

							$term_ids['term'][$value->term_id] = $term_info;
						}
						$term_ids['term'][$value->term_id]['count'] += 1;
					}
				}

			endwhile;

			wp_reset_query();
		}

		if ( $term_ids['term'] ) {

			// 歯抜けに作成した配列を詰める
			$term_ids['term'] = array_merge( $term_ids['term'] );

			// 投稿数でソート
			foreach ( $term_ids['term'] as $key => $value ) {
				$key_id[$key] = $value['count'];
			}
			array_multisort( $key_id, SORT_DESC, $term_ids['term'] );

			$term_ids['taxonomy'] = $taxonomy;

		}

	}

	return $term_ids;

}

2013/12/02 追記

70 行目で ‘count’ => $value->count でカウント数を取得していましたが、これではカテゴリ内記事の正確なカウント数を取得できないと分かったので下記に修正しました。

誤: 70 ‘count’ => $value->count (削除)
正: 74 $term_ids[‘term’][$value->term_id][‘count’] += 1; (追加)

一応、カテゴリ、タグどちらにでも効くように作ってます。以下の関連記事を元に取得する情報を増やした関数となります。

関連記事
WordPressでカテゴリに関連付いたタグ一覧を取得する

Ajax サーバ部分のコード

上記のコードは Ajax 経由で利用するので、javascript からのリクエストを処理できるように以下のアクションを WordPress に登録します。

function ajax_taxonomy_search() {

	$json = array(
		'status' => 'NG',
		'term'   => array()
	);

	header( 'Content-Type: application/json; charset=UTF-8' );

	if ( wp_verify_nonce( $_POST['token'], 'hogehoge' ) && isset( $_POST['term_id'] ) ) {

		// カテゴリに "すべて" が選択された場合
		// この場合、全てのタグをフォームに描画する
		if ( $_POST['term_id'] === 'all' ) {

			$tag_args = array(
				'orderby'         => 'count',
				'order'           => 'DESC'
			);

			$tags = get_tags( $tag_args );

			foreach ( $tags as $value ) {

				if ( ! isset( $json['taxonomy'] ) ) { 
					$json['taxonomy'] = $value->taxonomy;
				}

				$term_info = array(
					'id'    => $value->term_id,
					'name'  => $value->name,
					'slug'  => $value->slug,
					'count' => $value->count
				);
				array_push( $json['term'], $term_info );
			}

		// 渡されたターム ID が有効なら、一覧を返す
		} else {
			$json = get_relation_taxonomy( $_POST['term_id'] );
		}

		if ( $json['taxonomy'] ) {
			$json['status'] = 'OK';
		}

	}

	echo json_encode( $json );
	die();

}
add_action( 'wp_ajax_taxonomy_search', 'ajax_taxonomy_search' );
add_action( 'wp_ajax_nopriv_taxonomy_search', 'ajax_taxonomy_search' );

詳しい動作は以下の記事で記載しています。

関連記事
WordPressのデータをAjax経由で取得する

検索フォームを表示するコード

次は表示部分です。利便性を考えてショートコード化しています。

function code_taxonomy_search() {

	// category args
	$cat_args = array(
		'show_count'      => TRUE,
		'echo'            => FALSE,
		'id'              => 'category',
		'name'            => 'cat',
		'orderby'         => 'count',
		'order'           => 'DESC',
		'taxonomy'        => 'category',
		'show_option_all' => 'すべてのカテゴリ'
	);

	// tag args
	$tag_args = array(
		'orderby'         => 'count',
		'order'           => 'DESC'
	);

	$tags = get_tags( $tag_args );

	$form  = "<div id='search-box' style='margin-bottom: 28px;'>\n";
	$form .= "<form method='GET' action='" . home_url( '/' ) . "'>\n";

	$form .= wp_dropdown_categories( $cat_args ) . "\n";

	$form .= "<select id='post_tag' name='tag'>\n";
	$form .= "<option value=''>すべてのタグ</option>\n";
	foreach ( $tags as $value ) {

		$opt_val = esc_attr( $value->slug );
		$opt_str = esc_attr( $value->name . " (" . $value->count . ")" );

		$form .= "<option value='$opt_val'>$opt_str</option>\n";

	}

	$form .= "</select>\n";

	$form .= "<p><input id='submit' type='submit' value='検索' /></p>\n";
	$form .= "</form>\n";
	$form .= "</div>\n";

	return $form;

}
add_shortcode( 'taxonomysearch', 'code_taxonomy_search' );
add_filter('widget_text', 'do_shortcode');

以下の記事ほぼそのままのコードですが、投稿数の表示だったり降順ソートをしたりというのを追加しています。

関連記事
TwentyTwelveでカテゴリとタグで絞り込める検索フォームを作る

Ajax スクリプトの作成と WordPress の読み込み設定

最後に javascript を作成して WordPress に登録します。ファイル名は tsearch.js としました。

(function ($) {

	// カテゴリのボックス操作時に処理を開始
	$("#search-box select#category").change(function(){

		var term_id  = $(this).val();
		if (term_id == 0) { term_id = 'all'; }

		Search.term_id  = term_id;
		var result = sendData(Search);

		if (result !== false) {
			drawList(result);
		} else {
			emptyList();
		}

		return;
	});

	// Ajax 通信部分、変数を渡す絡みで同期型にした
	function sendData(param) {

		var result = false;

		$.ajax({
			type    : "POST",
			url     : param.url,
			dataType: "json",
			async   : false,
			data    : {
					action  : param.action,
					token   : param.token,
					term_id : param.term_id
			},
		}).done(function(callback){

			if ( callback != null && callback.status == 'OK' ) {
				result = callback;
			}

		}).fail(function(XMLHttpRequest, textStatus, errorThrown){

				// エラー時のデバッグ用
				//console.log(XMLHttpRequest);
				//console.log(textStatus);
				//console.log(errorThrown);

		});

		return result;
	}

	// 削除 -> 取得した JSON をセレクトボックスに追加
	function drawList(result) {

		var id = '#' + result.taxonomy;

		$(id).empty();
		$(id).append($('<option>').html('すべてのタグ').val(''));

		for (i = 0; i < result.term.length; i++) {
			var str = result.term[i].name + '  (' + result.term[i].count + ')';
			$(id).append($('<option>').html(str).val(result.term[i].slug));
		}

		return;
	}

	// 削除 -> "すべてのタグ" のみを表示
	function emptyList() {
		$("#post_tag").empty();
		$("#post_tag").append($('<option>').html('すべてのタグ').val(''));
		return;
	}

})(jQuery);

テーマ内の js/ ディレクトリに作成した後は以下の様な感じで登録します。

if ( ! is_admin() ) {

~ 省略 ~

	function register_taxonomy_script() {

		$jsdir  = get_stylesheet_directory_uri();
		$handle = 'taxonomy-search';

		wp_register_script(
			$handle,
			"$jsdir/js/tsearch.js",
			array( 'jquery' ),
			'1.0',
			true
		);

		wp_localize_script(
			$handle,
			'Search',
			array(
				'url'    => admin_url( 'admin-ajax.php' ),
				'action' => 'taxonomy_search',
				'token'  => wp_create_nonce( 'hogehoge' )
			)
		);

	}

~ 省略 ~

	function add_script() {

		register_taxonomy_script();
		wp_enqueue_script( 'taxonomy-search' );

	}
	add_action( 'wp_enqueue_scripts', 'add_script' );

}

長くなるので途中は省略していますが、全体的には以下とそう変わらないです。none 認証コードを作成するキーは「hogehoge」になっているので適当な文字列に変更しておくと良いと思います。

キーは ajax_taxonomy_search() 側の変更もお忘れなく。

関連記事
WordPressのデータをAjax経由で取得する

各種コードの追加はこれで以上です。

作成した検索フォームを表示する

検索フォームは以下のように関数名を直接テンプレートに書き込むか、ショートコード [[taxonomysearch]] で呼び出せます。

<?php echo code_taxonomy_search(); ?>

関数を直接書く場合は searchform.php を使うのがスマートかと思います。

関連記事
TwentyTwelveでカテゴリとタグで絞り込める検索フォームを作る

実際、なんてこと無い機能なのですがいざ作るとなると結構面倒です。お手軽に実装したい人はプラグインの方が良いかもしれませんね。

長くなりましたが、こんな感じになりました。カテゴリ連動型の検索フォームについてはこれで以上になります。

スポンサーリンク

コメント

  1. WP初心者 より:

    こんどワードプレスでサイトを作ろうと思っております。
    いろいろ検索してこちらにたどり着きました。

    サイト製作は2回目ですが、あまり知識がなく、ネットで探してきてはCSSなどコピーして使う程度です。

    カテゴリーとタグで同時に検索できるようにしたいので探していたところ、理想どうりの検索法にであえました。
    いくつか記事を拝見したのですが、専門的過ぎてあまり理解ができないのですが、どの記事から参考にして作るといいのでしょうか?
    アドバイスいただけると助かります。
    phpはテーマ編集から行うことはわかります。

    この機能がプラグインでできるとスゴイのですが・・・

    お時間をとらせてしまいますが、どうか助けていただけるとありがたいです。
    宜しくお願いします。

    • miura より:

      WP初心者 様

      コメントいただきありがとうございました。

      絞り込み機能を付けた連動型の検索フォームについてですが、作り方はこのページでまとめている内容のみとなります。

      各機能は分けて作成しているので、その課程を関連記事としてリンクを張ってはいますが途中変更していたりで実際には別物になっています。

      * 関連記事は主に WordPress 関数の動作検証等について書いていますので、各機能が何をしているかを把握する場合には多少は役に立つかもです^^;

      当ページのコードは基本コピペで動くはずですが、全て記載する必要がないものは省略していたりするので正直あまり親切なページではないのかもしれません。すみません。

      > この機能がプラグインでできるとスゴイのですが・・・

      自分用に作った物ですがプラグインは既にありますのでよければ使ってみてください。(プラグインの管理画面等は用意していないですが)

      ※ 記事のダウンロードリンクから落としてください。

      プラグイン化するために少しだけコードを変更しているのですが、インストールして表示させたい場所に下記ショートコードを貼り付ければ利用できるようになります。

      [taxonomy-search]

      また、プラグイン中の func-taxonomy-search.php というファイルでセキュリティ用の文字列を予め設定しているので、ご利用前にファイルを編集してランダムな文字列に変更しておくことをお勧めいたします。


      function get_nonce_key() {
      $nonce_key = 'HqMiiTOdL6goPxbyqrS8o';
      return $nonce_key;
      }

      * HqMiiTOdL6goPxbyqrS8o の部分です。

      スタイルシートは特に用意していないので、テーマ内のスタイルシート等でお好みにカスタマイズしてみてください。

      「WordPress 3.8」+「テーマ twentythirteen」で簡単に動作確認をしていますが何かあればご質問ください。

      よろしくお願いします。

  2. WP初心者 より:

    迅速な返信ありがとうございました。
    うまく反映されました。

    プラグインがあって大変助かりました。たぶん編集とかしてたら何日もかかったと思います。

    セキュリティーコードは適当に決めて変更したのでいいんですか?

    これからサイトを作りこんでいくので、またわからないことがあったら質問するかもしれません。
    そのときは、また宜しくお願いいたします。

    • miura より:

      WP初心者 様

      問題なく動作したようで良かったです。

      > セキュリティーコードは適当に決めて変更したのでいいんですか?

      はい、ユニークな文字列であれば大丈夫です。ランダムな物にしておくと良いと思います。

      分かる範囲での回答にはなりますが、質問等あればお気軽にどうぞ。

      よろしくお願いします。

  3. meg より:

    はじめまして。
    以前から連動して絞り込める検索フォームを設置したかったので、こちらの記事がとても参考になりました。

    また、こちらのフォームも素敵なのですが、「カテゴリ > タグ」以外にも
    「親カテゴリ > 子カテゴリ」 + 「キーワード入力」

    「親カテゴリ > 子カテゴリ > タグ」 + 「キーワード入力」
    などで連動して検索できる方法もあるととても便利だと感じました。

    • miura より:

      meg 様

      コメントありがとうございます。

      > 「親カテゴリ > 子カテゴリ」 + 「キーワード入力」
      > や
      > 「親カテゴリ > 子カテゴリ > タグ」 + 「キーワード入力」

      ご意見ありがとうございます。

      キーワード入力は少し考えていたのですが、サジェスト機能がないキーワード入力フォームは使いにくいという結論に至って省いていました。

      何か良い案が浮かんだらまた改良したいとは考えています。よろしくお願いします。

  4. ryouta より:

    はじめまして。
    すごいプラグインですね。
    ぜひこのプラグインを使用してみたいです。
    カテゴリ連動型プラグインはまだダウンロードできるでしょうか?

    • miura より:

      ryoutaさん

      コメントありがとうざいます。

      リンクが切れていたので再アップして記事内にダウンロードリンクを記載しました。
      こちらでお試しください。

  5. nemo より:

    ありがたくプラグインを使用させてもらっています。
    複数のカスタム投稿タイプを既存カテゴリーで部類しているのですが
    こちら特定のカテゴリーのみ除外する若しくは投稿タイプ毎で出しわける
    ような方法はありますでしょうか?
    ご教授願えましたら幸いです。

    • miura より:

      nemoさん

      コメントありがとうございます。
      質問の件ですが中のファイルを修正すれば勿論可能ですよ。

      1. 特定のカテゴリーのみ除外

      本記事の「カテゴリに関連するタグ情報を取得するコード」部分で対応可能です。
      WP_Query を使って検索をしているのでここで特定カテゴリを除外するオプションを追加して渡してあげます。(category__not_in パラメータとか…)

      2. 投稿タイプ毎で出しわける

      これも本コードのカスタマイズ次第では可能だと思うのですが、いずれもヒントは WP_Query を使うという事になります。
      デフォルトは「投稿 (post)」のみを引っ張るので、post_type パラメータで調整すると解決しそうです。

      ご参考までに。

      • nemo より:

        ご返信頂きありがとうございます。
        教えて頂いた1の方法を試してみようと思っているのですが、
        ”$args = array(‘category__not_in’ => array( 1,2,3)”
        のような形の引数記述をイメージしたのですが間違っていませんでしょうか。
        コードも書けない無知なものでプラグイン版で該当の位置が見つからず、試行錯誤しております、ご迷惑でなかったら、具体的な挿入位置を教えて頂けませんでしょうか?
        不躾に質問ばかりしてしまい申し訳ありません。

よく読まれている記事

  • 今日
  • 週間
  • 月間