投稿一覧に絞り込みを追加する


通常投稿のカテゴリーのドロップダウンのような絞り込みを、カスタム投稿タイプの投稿一覧画面に追加します。

完成図

列の追加方法は 管理画面の投稿一覧に列を追加する にて解説しています。

  1. 投稿一覧に列を追加する
  2. 投稿一覧の追加列にソート機能をつける
  3. 投稿一覧に絞り込みを追加する

投稿タイプなどの仕様は下記

  • 書籍 book 投稿タイプ
  • written_byカスタムフィールド、表示名「著者」
  • genre タクソノミー、表示ラベル「ジャンル」

使用するフック

無名関数を使ってひとまず、フックを動作させてみます。

add_action (
	'restrict_manage_posts',
	function ( $post_type ) {
		if( 'book' === $post_type ){
			echo "restrict_manage_posts フックはここです";
		}
	}
);

restrict_manage_posts フックを使うと、この位置に HTML を出力できます。

タクソノミーでの絞り込み

タクソノミーのセレクターを作るのは、WordPress のコア関数でできます。

wp_dropdown_categories() – Function | Developer.WordPress.org

taxonomy string|array

Name of the taxonomy or taxonomies to retrieve. Default 'category'.

引数に任意のタクソノミーを設定できます。

この関数をフックに組み込んでみます。
書籍投稿の一覧でのみ処理が実行されるよう、まず条件判定(自分はこういう時は、早期リターンをつかいます)次に引数の配列、最後にドロップダウンセレクターを出力する関数。

add_action (
	'restrict_manage_posts',
	function ( $post_type ) {
		if( 'book' !== $post_type ){
			return;
		}

		$args = array(
			'name'              => 'genre',
			'get'               => 'all',
			'hierarchical'      => true,
			'option_none_value' => '',
			'show_option_none'  => 'すべてのジャンル',
			'taxonomy'          => 'genre',
			'value_field'       => 'slug',
		);

		wp_dropdown_categories( $args );
	}
);

「すべてのジャンル」というセレクターが増えました。

ジャンルを選んで「絞り込み」を実行してみます。

ジャンルごとに絞り込み表示ができました。

Query Monitor でクエリ変数など確認してみましょうgenre というクエリ変数に "sf" という値が格納されています。

これを元に、コアの機能でタクソノミーでの絞り込みクエリが実行されます。

最後に、セレクターがデフォルトの「すべてのジャンル」に戻っているので調整します。これは wp_dropdown_categories() の引数の selected を指定してやればいいので、下記を追記。

'selected' => get_query_var( 'genre' ) ?: '',

エルビス演算子を使って、 get_query_var( 'genre' ) に値があればそのままセット、genre クエリ変数がなければ空文字を引数にセットするように追記します。これで Warning を抑止しつつ、可読性の高いコードにできます。

最終的なコード

add_action (
	'restrict_manage_posts',
	function ( $post_type ) {
		if( 'book' !== $post_type ){
			return;
		}

		$args = array(
			'name'              => 'genre',
			'get'               => 'all',
			'hierarchical'      => true,
			'option_none_value' => '',
			'show_option_none'  => 'すべてのジャンル',
			'taxonomy'          => 'genre',
			'value_field'       => 'slug',
			'selected'          => get_query_var( 'genre' ) ?: '',
		);

		wp_dropdown_categories( $args );
	}
);

カスタムフィールドでの絞り込み

こちらはカスタムフィールドの値の取得、セレクターの HTML 生成などを独自に実装する必要があります。下記の順で実装していきます。

  1. カスタムフィールドの値をすべて取得
  2. セレクタの HTML を出力
  3. クエリ変数を追加する
  4. メインクエリを調整する

カスタムフィールドの値をすべて取得する

下準備として、入力されているカスタムフィールドの値をすべて取得する方法を検討します。一発で取り出す関数は WordPress コアには用意されていません。

SQL で取得する方法

ほぼ一発で出せます。

global $wpdb;
$meta_table = $wpdb->prefix . 'postmeta';
$rows = $wpdb->get_results( "SELECT DISTINCT meta_value FROM $meta_table WHERE meta_key='written_by'", ARRAY_A);

$all_written_by = array_map( function( $row ) {
			return $row["meta_value"];
		}, $rows ?? array() );

ただし、SQL を使うとセキュリティ上、注意すべきことが増えます。これを参考にして実装されたコードによって WordPress の破壊や不正アクセスなどの被害が生じても、当サイトは一切の責任を負えません。

get_posts を使う方法

get_posts と get_post_meta を組み合わせてもできます。

  1. written_by フィールドを持つ投稿を取得
  2. 1 で得た投稿の written_by フィールドの値を取得する
  3. array_unique で重複を取り除く
$posts_has_written_by = get_posts( array(
				'posts_per_page' => -1,
				'post_type'  => 'book',
				'meta_query' => array( array(
					'meta_key' => 'written_by',
					'compare'  => 'EXISTS'
				))
			));

$all_written_by = array_unique(
			array_map(
				function( $post ) {
					return get_post_meta( $post->ID, 'written_by', true );
				},
				$posts_has_written_by ?? array()
			)
		);

1 で取得した投稿の数だけ、データベースへの問い合わせが発生するので、パフォーマンスは SQL を使うより悪くなります。管理画面だけのことだし、データベースが最新バージョンなら、気になるほど速度も悪化しないとは思います。

$posts_has_written_by ?? array()

null 合体演算子と空配列を組み合わせると get_posts で投稿が取得できていない時に Warning が発生しません。$posts_has_written_by に何も入ってなければ、array_map 関数には空配列が渡されます。

PHP: 比較演算子 null 合体演算子 – Manual

セレクタの HTML を出力

selectoption 要素を出力します。

add_action(
	'restrict_manage_posts',
	function ( $post_type ) {

		// すべての著者の値が入った配列 $all_written_by を作る.

		natcasesort( $all_written_by );

		?>
		<select name="written_by">
			<?php foreach ( $all_written_by as $written_by ) : ?>
				<option value="<?php echo esc_attr( $written_by ); ?>" >
					<?php echo esc_html( $written_by ); ?>
				</option>
			<?php endforeach; ?>
		</select>

		<?php
	}
);

クエリ変数を追加する

「絞り込み」を実行すると、下記のような URL パラメターになります。ただし、クエリ変数には何も追加されていません。

edit.php?s&post_status=all&post_type=book&action=-1&m=0&genre
&written_by=アーサー・C・クラーク&filter_action=絞り込み
&paged=1&action2=-1

Query Monitor で確認します。

このままではメインクエリを調整するために URLパラメターから著者名が読み出せません。そこで、クエリ変数に written_by を追加してやります。

add_filter(
	'query_vars',
	function ( $qvars ) {
		$qvars[] = 'written_by';
		return $qvars;
	}
);

get_query_var( 'written_by' ) で、「アーサー・C・クラーク」など著者名が取得できるようになりました。

「アーサー・C・クラーク」で絞り込みをしても、セレクターがリセットされているのも修正できます。option タグの selectedプロパティを出力するよう変更。

 <?php echo ( $selected === $written_by ) ? 'selected="selected"' : ''; ?>

HTML 出力の処理は下記になります。

	$selected = get_query_var( 'written_by' ) ?: '';

?>
		
<select name="written_by">
	<option value="">すべての著者</option>
	<?php foreach ( $all_written_by as $written_by ) : ?>
		<option value="<?php echo esc_attr( $written_by ); ?>"
			<?php echo ( $selected === $written_by ) ? 'selected="selected"' : ''; ?> >
				<?php echo esc_html( $written_by ); ?>
		</option>
	<?php endforeach; ?>
</select>

メインクエリを調整する

pre_get_posts でメインクエリーを調整して、 get_query_var( 'written_by' ) で取得した著者の投稿のみを表示するようします。

書籍投稿一覧画面でのみ処理を実行するよう判定を実装

add_action(
	'pre_get_posts',
	function ( $query ) {
		if ( ! is_admin()
		|| ! $query->is_main_query()
		|| ! isset( $query->query['post_type'] ) ) {
			return;
		}

		if ( 'book' !== $query->query['post_type'] ) {
			return;
		}
		
		if( ! isset( $query->query['written_by'] ) ) {
			return;
		}

        // ここにメインクエリーの調整を実装.

		return;
	},
);

カスタムフィールドに基づいて投稿を抽出したいので、追加するのは メタクエリー です。

$args = array( array(
					"key"     => "written_by",
					"value"   => $query->query['written_by']
));

$query->set( 'meta_query', $args );

compare キーは、デフォルトでは = なので省略可能。

完成

最終的なコード

<?php

add_action (
	'restrict_manage_posts',
	function ( $post_type ) {
		if( 'book' !== $post_type ){
			return;
		}

		$args = array(
			'name'              => 'genre',
			'get'               => 'all',
			'hierarchical'      => true,
			'option_none_value' => '',
			'show_option_none'  => 'すべてのジャンル',
			'taxonomy'          => 'genre',
			'value_field'       => 'slug',
			'selected'          => get_query_var( 'genre' ) ?: '',
		);

		wp_dropdown_categories( $args );
	}
);

add_action (
	'restrict_manage_posts',
	function ( $post_type ) {
		if( 'book' !== $post_type ){
			return;
		}

		$posts_has_written_by = get_posts( array(
			'posts_per_page' => -1,
			'post_type'  => 'book',
			'meta_query' => array( array(
				'meta_key' => 'written_by',
				'compare'  => 'EXISTS'
			))
		));

		$all_written_by = array_unique(
			array_map(
				function( $post ) {
					return get_post_meta( $post->ID, 'written_by', true );
				},
				$posts_has_written_by ?? array()
			)
		);

		natcasesort( $all_written_by );

		$selected = get_query_var( 'written_by' ) ?: '';

		?>
		
		<select name="written_by">
			<option value="">すべての著者</option>
			<?php foreach ( $all_written_by as $written_by ) : ?>
				<option value="<?php echo esc_attr( $written_by ); ?>"
					<?php echo ( $selected === $written_by ) ? 'selected="selected"' : ''; ?> >
						<?php echo esc_html( $written_by ); ?>
				</option>
			<?php endforeach; ?>
		</select>

		<?php
	}
);

add_filter(
	'query_vars',
	function ( $qvars ) {
		$qvars[] = 'written_by';
		return $qvars;
	}
);

add_action(
	'pre_get_posts',
	function ( $query ) {
		if ( ! is_admin()
		|| ! $query->is_main_query()
		|| ! isset( $query->query['post_type'] ) ) {
			return;
		}

		if ( 'book' !== $query->query['post_type'] ) {
			return;
		}
		
		if( ! isset( $query->query['written_by'] ) ) {
			return;
		}

		$meta_query = array( array(
						"key"     => "written_by",
						"value"   => $query->query['written_by'],
		));

		$query->set( 'meta_query', $meta_query );

		return;
	}
);

カテゴリー:

最終更新: