カスタム投稿タイプのアーカイブ表示で、カスタム分類での絞り込み検索を行う

Webサイト上で絞り込み検索ができるようにしたい」というのは比較的よくある要望だと思います。そんな場合どうしてますか?カスタム投稿タイプだったら、アーカイブ表示の際に、少々工夫すれば絞り込みを行うことができてしまうのです。

この記事は、jim912によって書かれたものです。
引用を行う場合は、著作権法に定める範囲にて、引用元であるSimple Colorsの出展元表示とhttp://www.warna.info/archives/2421/へのリンクを必ず行って下さい。

WordPress では、パーマリンクの設定如何に関わらず、?cat=5 などといったパラメーターを追加すると絞り込みが可能となっています。(なので、カテゴリー内でのテキスト検索といったことも簡単に可能なんですよ?)
このため、フォームの method 属性を get にしておけば、比較的簡単に絞り込みができてしまうのですが、この場合、アドレス欄にパラメーターがずらずら並んでしまってクライアント受けが良くないのと、チェックボックスによる複数項目での検索に対応しづらいのが難点です。

これを post で投げて、アドレスはそのままで絞り込み検索、さらにはページ送りにも対応させてしまいましょう。

フォームの method を post にする場合、URLによるパラメーターの引き継ぎが出来ないため、そのままではページ送りができません。これについては、検索された際の条件を cookie に保存し、2ページ目以降はそれを利用するようにします。条件に該当するものが送信されてきた際に、setcookie 関数を使って、cookie に保存してしまいます。送信されたデータが配列であり得る場合は、serialize 関数で文字列化しておくと便利です。

複数項目の検索は、pre_get_posts フックにて、送信されたデータを複数タクソノミークエリーに変換してあげれば、後は WordPress が自然に条件にあったものを取得してくれます。あと、ひと工夫として、送信する項目名によって、複数タクソノミークエリーの operator も設定できるようにしてみます。

{taxonomy}__{operator}[]

フォームの name 属性を上記のようにすることで、カスタム分類の検索を「いずれかに該当」「いずれにも該当しない」「全てに該当」と選べるようにしてみます。ちょうど、WP_Query のカテゴリーパラメーターの category__in、category__not_in、category__and のような感じです。

あと、もう1点の問題として、検索してページ送りで移動し、1ページ目に戻ってきた場合の表示です。
検索前は、cookie が残っていた場合でも、それで絞り込んでしまってはまずいですし、2ページ目から1ページ目に遷移した際に検索条件が外れてしまってもいけません。
これを解消するために、2ページ目から1ページ目に遷移した場合、通常は paged パラメーターは消えますが、/page/1 のように、paged パラメーターを残すようにして判別できるようにします。

ちょっと長めですが、下記のコードを functions.php に追加してください。

/*
 * カスタム投稿タイプのアーカイブページで、カスタム分類による複合検索を可能にします。
 * フォームのname属性は、カスタム分類のスラッグ + アンダースコア2個(__) + tax_queryのオペレーター + []となります。
 * オペレーターは、以下の3種類が指定可能です。
 *   in     : いずれかに合致
 *   not_in : いずれにも合致しない
 *   and    : 全てに合致
 * カスタム分類のスラッグが color でいずれかに合致させたい場合、フォームのname属性は color__in[] です。
 * 検索条件は、cookieに保存され、ページ送りを行った際にも条件を引き継ぐようになります。
 */
function custom_post_type_search( $wp_query ) {
	global $search_cond;
	$search_cond = array();
	if ( ! is_admin() && $wp_query->is_main_query() ) {
		if ( $wp_query->is_post_type_archive() ) {
			$post_type = $wp_query->get( 'post_type' );
			$taxonomies = get_object_taxonomies( $post_type );
			if ( $taxonomies ) {
				$tax_query = array();
				foreach ( $taxonomies as $taxonomy ) {
					foreach ( array( 'in', 'not_in', 'and' ) as $operator ) {
						$_post = array();
						$name = $taxonomy . '__' . $operator;
						$cookie_name = $post_type . '-' . $name;
						if (
							( $_SERVER['REQUEST_METHOD'] == 'POST' )
							||
							( get_query_var( 'paged' ) && isset( $_COOKIE[$cookie_name] ) && ! empty( $_COOKIE[$cookie_name] ) )
						) {
							if ( $_SERVER['REQUEST_METHOD'] == 'POST' ) {
								if ( isset( $_POST[$name] ) && ! empty( $_POST[$name] ) ) {
									$_post = stripslashes_deep( $_POST[$name] );
									setcookie( $cookie_name, serialize( $_post ), 0, '/' );
								} elseif ( isset( $_COOKIE[$cookie_name] ) ) {
									setcookie( $cookie_name, serialize( $_post ), 0, '/' );
								}
							} else {
								$_post = maybe_unserialize( stripslashes_deep( $_COOKIE[$cookie_name] ) );
							}
							if ( $_post ) {
								$tax_query[] = array(
									'taxonomy' => $taxonomy,
									'terms' => $_post,
									'field' => 'slug',
									'operator' => strtoupper( str_replace( '_', ' ', $operator ) )
								);
								$search_cond[$name] = $_post;
							}
						}
					}
				}
				$wp_query->set( 'tax_query', $tax_query );
			}
		}
	}
}
add_action( 'pre_get_posts', 'custom_post_type_search' );

/*
 * WordPress では、本来アーカイブの1ページ目でページ送りのパラメーターがあると、パラメーターなしの
 * ページにリダイレクトをするようになっています。この場合、最初に訪れたのか検索後の表示かが判別できないため
 * 1ページ目でページ送り用のパラメーターが付いている場合のリダイレクトを停止させます。
 */
function fix_first_paged_redirect( $redirect_url, $requested_url ) {
	if ( is_post_type_archive() && get_query_var( 'paged' ) == 1 ) {
		$redirect_url = $requested_url;
	}
	return $redirect_url;
}
add_filter( 'redirect_canonical', 'fix_first_paged_redirect', 10, 2 );

/*
 * ページ送りのリンクで1ページ目にパラメーターが付くようにします。
 * これによって、最初に訪れたのか、検索後に戻ってきたのかを判断できるようになります。
 */
function add_first_paged_query( $result ) {
	global $wp_rewrite;
	if ( is_post_type_archive() ) {
		if ( $wp_rewrite->using_permalinks() ) {
			if ( ! preg_match( '#/page/[0-9]+/?#', $result ) ) {
				$result = user_trailingslashit( rtrim( $result, '/' ) . '/page/1' );
			}
		} elseif ( ! preg_match( '#(&|\?)paged=[0-9]+#', $result ) ) {
			$result = add_query_arg( array( 'paged' => 1 ), $result );
		}
	}
	return $result;
}
add_filter( 'get_pagenum_link', 'add_first_paged_query' );
カテゴリー: WordPressの小技 タグ: , , , , パーマリンク

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です