シンプルすぎるWordPressの一覧画面を使いやすくする、の第二弾です。
今回は、投稿一覧画面の絞り込み機能を拡張していきます。
投稿一覧テーブルの上のほうに、デフォルトでは「投稿日時」や「カテゴリー」で絞り込みできるプルダウンが並んでいますが、記事に設定されている値で絞り込みをするプルダウンを追加していきます。
第一弾の記事で投稿一覧画面に列項目を追加しましたが、そのコードをベースに作成していくことにします。
第一弾の記事はこちら↓
手順としては以下の流れです。
1
プルダウンリストを作成する
まずは、プルダウンリストを作成していきます。
デフォルトで表示されている「すべての日時」や「カテゴリー一覧」の横に追加していくイメージです。
今回は、「出版社」「値段」「在庫状況」のプルダウンリストを作成します。
基本となる大枠のコードは以下のとおりです。
add_action(
//第一引数 = アクションフック名
'restrict_manage_posts',
//第二引数 = 処理内容
function($post_type,$which){
//実際の処理内容
},
//第三引数 = 実行する優先順位
10,
//第四引数 = 第二引数で受け取る引数の数
2
);
アクションフックを実行するadd_action関数の第一引数には、一覧画面の絞り込みボタンを作成するrestrict_manage_postsというアクションフックを指定します。
第二引数は、プルダウンを作成するコールバック関数を記述します。この関数では2つの引数を受け取ることができます。ひとつは投稿タイプ($post_type)で、もうひとつ($which)はプルダウンリストを一覧テーブルの上に表示するか下に表示するかの指定ができるようです。
第三引数は、優先度を指定。デフォルトは10です。
第四引数は、第二引数の関数で受け取る引数の数を指定します。デフォルトは1です。2つの引数を受け取る場合は2を指定します。
第二引数のコールバック関数を外部に出す書き方もできます。
add_action('restrict_manage_posts','コールバック関数名');
function コールバック関数名($post_type){
//実際の処理内容
}
今回、引数$whichは使用しないので、第三引数と第四引数は省略することができます。省略した場合はデフォルト値が設定されます。
コールバック関数に実際の処理を記述していきます。大枠は以下の形です。
function add_select_filters($post_type){
if($post_type === 'post'){
//$post_typeが投稿の場合の処理内容
}
}
$post_typeが投稿(post)の場合に{}内に記述された処理が実行されるというコードです。
もしも固定ページやカスタム投稿タイプの一覧画面でも同じ絞り込みボタンを作成したい場合には条件分岐は必要ありません。
処理内容の流れを簡単に説明すると「プルダウンリストにするデータを取得」して、「そのデータをもとにプルダウンリストを作成するhtmlを記述」する、ということになります。
順番が前後しますが、まずは「在庫状況」のリスト作成のコードを紹介します。
「在庫状況」の場合
具体的なコードは以下の通りです。
$stock_choices = get_field_object('stock')['choices'];
$stock_selected_value = filter_input( INPUT_GET, 'stock');
echo '<select name="stock" id="stock">';
echo '<option value="">在庫状況</option>';
foreach($stock_choices as $key => $value){
$stock_selected = selected( $stock_selected_value, $key, false );
echo '<option value="'.$key.'"'.$stock_selected.'>'.$value.'</option>';
}
echo '</select>';
「在庫状況」は選択肢型のカスタムフィールドを使用して作成しています。
1行目で「プルダウンリストにするデータを取得」しています。
get_field_object()はACFの独自関数で、カスタムフィールドに関するさまざまな情報を取得できるコードです。
get_field_object('stock')['choices']
で、stockというカスタムフィールド名の、すべての選択肢(choices)データを取得できる、とても便利な関数です。
選択肢データは、
選択肢のスラッグ => 選択肢のラベル
という形の配列になっています。
2〜9行目は「プルダウンリストを作成するhtml」。
2行目は、「絞り込み」ボタンを押した際に、プルダウンで選ばれている値を取得しています。’stock’の部分はカスタムフィールドの名称を入れてください。
5〜8行目は1行目で取得したデータをループで回してリストを作成するコードです。
6行目では2行目で得られた値とループを回して得られた値とを比較して同じであればselected属性を付与するコードです。7行目で使用します。
「在庫状況」のプルダウンリストはこれで完成です。
「在庫状況」のような選択肢型のカスタムフィールドやタクソノミーなど、値が限定されているものであればそれを取得して並べるだけなのですが、「出版社」や「値段」といった自由入力型のカスタムフィールドの場合には少し手間が必要です。
「出版社」「値段」の場合
「出版社」の場合は以下のコードになります。
$pub_posts = get_posts([
'post_type' => 'post',
'posts_per_page' => -1,
'post_status' => 'publish',
]);
wp_reset_postdata();
$pub_items = [];
foreach($pub_posts as $pub_post){
$pub_item = get_post_meta($pub_post->ID,'publisher',true);
if(!in_array($pub_item,$pub_items)){
$pub_items[] = $pub_item;
}
}
$pub_selected_value = filter_input( INPUT_GET, 'publisher');
echo '<select name="publisher" id="publisher">';
echo '<option value="">すべての出版社</option>';
foreach($pub_items as $key => $pub_value){
$pub_selected = selected( $pub_selected_value, $pub_value, false );
echo '<option value="'.$pub_value. '"'.$pub_selected. '>'.$pub_value.'</option>';
}
echo '</select>';
すべての投稿を取得して(1〜8行目)、そのデータをループで回して「出版社」のカスタムフィールドのデータを取得して9行目で作成した配列に入れています(9〜15行目)。
12〜14行目では、9行目で作成した$pub_itemsという配列に取得した値($pub_item)が存在しなければ$pub_itemsに$pub_itemを入れるという意味合いです。これで一意のデータが並ぶ配列になります。
17〜24行目は「プルダウンリストを作成するhtml」ですが、こちらは「在庫状況」の場合とほぼ同じです。
ということで全体のコードは以下になります。
※functions.phpを編集する際には必ずバックアップをとり、すぐに元に戻せる状況にしておいてください。functions.phpの記述に誤りがあると、最悪、サイト全体が表示されなくなります。
add_action('restrict_manage_posts', 'add_select_filter');
function add_select_filter($post_type){
if ($post_type === 'post'){
//出版社
$pub_posts = get_posts([
'post_type' => 'post',
'posts_per_page' => -1,
'post_status' => 'publish',
]);
wp_reset_postdata();
$pub_items = [];
foreach($pub_posts as $pub_post){
$pub_item = get_post_meta($pub_post->ID,'publisher',true);
if(!in_array($pub_item,$pub_items)){
$pub_items[] = $pub_item;
}
}
$pub_selected_value = filter_input( INPUT_GET, 'publisher');
echo '<select name="publisher" id="publisher">';
echo '<option value="">すべての出版社</option>';
foreach($pub_items as $key => $pub_value){
$pub_selected = selected( $pub_selected_value, $pub_value, false );
echo '<option value="'.$pub_value. '"'.$pub_selected. '>'.$pub_value.'</option>';
}
echo '</select>';
//値段
$price_posts = get_posts([
'post_type' => 'post',
'posts_per_page' => -1,
'post_status' => 'publish',
]);
wp_reset_postdata();
$price_items = [];
foreach($price_posts as $price_post){
$price_item = get_post_meta($price_post->ID,'price',true);
if(!in_array($price_item,$price_items)){
$price_items[] = $price_item;
}
}
$price_selected_value = filter_input( INPUT_GET, 'price');
echo '<select name="price" id="price">';
echo '<option value="">すべての値段</option>';
foreach($price_items as $price_value){
$price_selected = selected( $price_selected_value, $price_value, false );
echo '<option value="'.$price_value. '"'.$price_selected. '>'.$price_value.'円</option>';
}
echo '</select>';
//在庫状況
$stock_choices = get_field_object('stock')['choices'];
$stock_selected_value = filter_input( INPUT_GET, 'stock');
echo '<select name="stock" id="stock">';
echo '<option value="">在庫状況</option>';
foreach($stock_choices as $key => $value){
$stock_selected = selected( $stock_selected_value, $key, false );
echo '<option value="'.$key.'"'.$stock_selected.'>'.$value.'</option>';
}
echo '</select>';
}
}
ただし、上記のコードは同じループを複数回しているなど、ムダが多いコードです。
投稿の数が多くなるとパフォーマンスに影響がでる可能性があります。
修正したコードが以下です。
プルダウン作成用の関数を別途用意して共通部分はそこにまとめました。
だいぶスッキリしたと思います。
※functions.phpを編集する際には必ずバックアップをとり、すぐに元に戻せる状況にしておいてください。functions.phpの記述に誤りがあると、最悪、サイト全体が表示されなくなります。
add_action('restrict_manage_posts', 'add_select_filters');
function add_select_filters($post_type){
if($post_type === 'post'){
$posts = get_posts([
'post_type' => 'post',
'posts_per_page' => -1,
'post_status' => 'publish',
]);
wp_reset_postdata();
add_custom_filter('publisher','すべての出版社','',$posts);
add_custom_filter('price','すべての値段','円',$posts);
add_choices_filter('stock','すべての在庫状況');
}
}
// 自由入力型カスタムフィールド用の関数
function add_custom_filter($meta_key,$label,$after_text,$posts) {
$values = [];
foreach($posts as $post){
$value = get_post_meta($post->ID,$meta_key,true);
if(!in_array($value,$values)){
$values[] = $value;
}
}
$selected_value = filter_input(INPUT_GET, $meta_key);
echo '<select name="' . esc_attr($meta_key) . '" id="' . esc_attr($meta_key) . '">';
echo '<option value="">' . esc_html($label) . '</option>';
foreach ($values as $value) {
$selected = selected($selected_value, $value, false);
echo '<option value="' . esc_attr($value) . '"' . $selected . '>' . esc_html($value) . esc_attr($after_text) . '</option>';
}
echo '</select>';
}
// 選択肢型カスタムフィールド用の関数
function add_choices_filter($meta_key,$label) {
$stock_choices = get_field_object($meta_key)['choices'];
$selected_value = filter_input(INPUT_GET, $meta_key);
echo '<select name="' . esc_attr($meta_key) . '" id="' . esc_attr($meta_key) . '">';
echo '<option value="">' . esc_html($label) . '</option>';
foreach ($stock_choices as $key => $value) {
$selected = selected($selected_value, $key, false);
echo '<option value="' . esc_attr($key) . '"' . $selected . '>' . esc_html($value) . '</option>';
}
echo '</select>';
}
これでプルダウンリストの作成は完了です。
ただ、カスタムフィールドを対象にしたプルダウンは、まだリストができただけで、絞り込みの機能はできていない状態です。
2
プルダウンで選ばれた項目で
絞り込む機能を実装する
それではカスタムフィールドのプルダウンに絞り込み機能を実装していきましょう。
その前に、一覧画面がどのように表示されているのか、少し解説を。
一覧画面は投稿タイプに関わらずedit.phpによって表示されています。
一覧画面のURLを見ると、固定ページの一覧画面の場合はedit.php?post_type=page、カスタム投稿の一覧画面の場合はedit.php?post_type=カスタム投稿スラッグ、という具合にパラメータが付加されているはずです。
つまり一覧画面は、ページを表示するためのメインクエリにて投稿タイプごとに絞り込みがかけられていて、該当の投稿タイプのみ表示されているというわけです。
(投稿の一覧画面の場合、パラメータは省略されているようですね)
ここで、デフォルトで設置されているプルダウンを使用して、絞り込みをしてみてください。
上記のパラメータの後にさらにいくつかのパラメータが追加されていることがわかります。
このパラメータは実のところWP_Queryのパラメータと同様です。
絞り込み機能の実装は、メインクエリの絞り込み条件にプルダウンリストごとのパラメータを追加していくということになります。
基本となる大枠のコードは以下のとおりです。
add_action(
//第一引数 = アクションフック名
' pre_get_posts',
//第二引数 = 処理内容
function($query){
//絞り込み条件の設定
},
//今回、第二引数の関数で受け取る引数がひとつのため
//第三引数と第四引数は省略しています。
);
使用するアクションフックはpre_get_postsで、これはメインクエリを実行する前に発生するフックです。
第二引数のコールバック関数ではメインクエリのクエリ条件を受け取ることができます。
実際のコードは以下のとおりです。意図がわかりやすいように整理していないコードです。
※functions.phpを編集する際には必ずバックアップをとり、すぐに元に戻せる状況にしておいてください。functions.phpの記述に誤りがあると、最悪、サイト全体が表示されなくなります。
add_action( 'pre_get_posts', 'pre_get_posts_admin' );
function pre_get_posts_admin( $query ) {
if ( is_admin() && $query->is_main_query() && 'post' === $query->get('post_type')) {
//meta_queryの準備
$meta_query = $query->get('meta_query');
if(!is_array($meta_query)){
$meta_query = [ 'relation' => 'AND' ];
}
//出版社
$pub_selected_value = filter_input( INPUT_GET, 'publisher');
if(!empty($pub_selected_value)){
$meta_query[] = [
'key' => 'publisher',
'value' => $pub_selected_value,
'compare' => '='
];
}
//値段
$price_selected_value = filter_input( INPUT_GET, 'price');
if(!empty($price_selected_value)){
$meta_query[] = [
'key' => 'price',
'value' => $price_selected_value,
'compare' => '='
];
}
//在庫状況
$stock_selected_value = filter_input( INPUT_GET, 'stock');
if(!empty($stock_selected_value)){
$meta_query[] = [
'key' => 'stock',
'value' => $stock_selected_value,
'compare' => '=',
];
}
if(count($meta_query) > 1){
$query->set('meta_query',$meta_query);
}
}
}
まず、注意点としてpre_get_postsはフロントも含めて全てのページに影響するアクションフック。ですので、特定のページのみで絞り込みするように条件分岐させることが重要です。
今回のケースでは、4行目のように管理画面かつメインクエリかつ投稿タイプが投稿の画面で適用されるようにしています。
メインクエリでカスタムフィールドによる絞り込みをする際には、配列meta_queryでの設定が必要になります。また、複数のカルタムフィールドによる絞り込みの際には、’relation’の設定が必要になります。 7〜10行目がその設定になります。meta_queryが配列でない場合には配列にして、
'relation' => 'AND'
の設定を含めるようにしています。
各プルダウンリストの絞り込み条件は、このmeta_queryに追加していく形になります。
12〜40行目は「出版社」「値段」「在庫状況」それぞれのコードです。
出版社の部分を例に見てみましょう。
13行目は前の手順でも登場しました。絞り込みボタンが押された際に、プルダウンリストで選択されている値を取得するコードです。
その値が空でなかったら、つまり、なにかプルダウンリストで選ばれていたらという条件分岐を14行目で行い、15〜19行目の$meta_queryへの設定を行っています。
「値段」「在庫状況」も同じ処理を行っています。
そして、43〜45行目で12〜40行目での設定を引数$queryにセットしています。
これを整理したのが以下のコードです。
※functions.phpを編集する際には必ずバックアップをとり、すぐに元に戻せる状況にしておいてください。functions.phpの記述に誤りがあると、最悪、サイト全体が表示されなくなります。
add_action( 'pre_get_posts', 'pre_get_posts_admin' );
function pre_get_posts_admin( $query ) {
if ( is_admin() && $query->is_main_query() && 'post' === $query->get('post_type')) {
$inputs = [
'publisher' => filter_input( INPUT_GET, 'publisher'),
'price' => filter_input( INPUT_GET, 'price'),
'stock' => filter_input( INPUT_GET, 'stock')
];
$meta_query = $query->get('meta_query');
if(!is_array($meta_query)){
$meta_query = [ 'relation' => 'AND' ];
}
foreach($inputs as $key => $value){
if(!empty($value)){
$meta_query[] = [
'key' => $key,
'value' => $value,
'compare' => '='
];
}
}
if(count($meta_query) > 1){
$query->set('meta_query',$meta_query);
}
}
}
かなりスッキリしました。
これでカスタムフィールドでの絞り込み機能も実装することができました。
3
(必要であれば)
デフォルトのプルダウンを非表示に
まあ、表示したままでもいいのですが、もしも「日付で絞り込みはしない」とか「カテゴリーで絞り込みしない」という場合には、デフォルトで表示されている「すべての日付」「カテゴリー一覧」のプルダウンはじゃまなので非表示にしちゃいましょう。
投稿日時のプルダウンはdisable_months_dropdownというフィルターフックを、
カテゴリーのプルダウンはdisable_categories_dropdownというフィルターフックを、
それぞれ使用します。
※functions.phpを編集する際には必ずバックアップをとり、すぐに元に戻せる状況にしておいてください。functions.phpの記述に誤りがあると、最悪、サイト全体が表示されなくなります。
add_filter('disable_months_dropdown','remove_dropdown',10,2);
add_filter('disable_categories_dropdown','remove_dropdown',10,2);
function remove_dropdown($disable,$post_type){
if($post_type === 'post'){
$disable = true;
}
return $disable;
}
add_filter関数の第二引数は、trueを返すコールバック関数を設定しています。
add_filter('disable_months_dropdown',true);
第二引数のコールバック関数は第一引数を有効にするかどうかの$disableと$post_typeの2つの引数を受け取ることができます。(ですので第四引数が2になっています)
今回、$post_typeが投稿の一覧画面の場合はtrueを返すという関数をつくって、2つのadd_filter関数で共用しています。
検索してもあまりヒットしない内容ですので、参考にしてみてください。
まとめ
今回は、カスタムフィールドでの絞り込みを行いましたが、例えば、
投稿ステータス(’post_status’)で絞り込みをかけて下書きの投稿だけ表示する、とか、
著者(’author’)で絞り込みをかけて、特定の著者の投稿だけ表示する、とか、
日付(’date’)を加工して直近3ヶ月以内の投稿だけ表示する、とか、
いろいろと便利に活用できそうです。
これらはひとつめの手順だけで実装できますので試してみてください。
もしもうまくいかない場合には、WP_Query でのパラメータ設定を参照にしてみてください。
こちらの記事では並べ替え機能の拡張を紹介しています↓
今回のすべてのコードを以下にまとめます。
※functions.phpを編集する際には必ずバックアップをとり、すぐに元に戻せる状況にしておいてください。functions.phpの記述に誤りがあると、最悪、サイト全体が表示されなくなります。
/*----------------
* プルダウンリストを作成する
*----------------*/
add_action('restrict_manage_posts', 'add_select_filters');
function add_select_filters($post_type){
if($post_type === 'post'){
$posts = get_posts([
'post_type' => 'post',
'posts_per_page' => -1,
'post_status' => 'publish',
]);
wp_reset_postdata();
add_custom_filter('publisher','すべての出版社','',$posts);
add_custom_filter('price','すべての値段','円',$posts);
add_choices_filter('stock','すべての在庫状況');
}
}
// 自由入力型カスタムフィールド用の関数
function add_custom_filter($meta_key,$label,$after_text,$posts) {
$values = [];
foreach($posts as $post){
$value = get_post_meta($post->ID,$meta_key,true);
if(!in_array($value,$values)){
$values[] = $value;
}
}
$selected_value = filter_input(INPUT_GET, $meta_key);
echo '<select name="' . esc_attr($meta_key) . '" id="' . esc_attr($meta_key) . '">';
echo '<option value="">' . esc_html($label) . '</option>';
foreach ($values as $value) {
$selected = selected($selected_value, $value, false);
echo '<option value="' . esc_attr($value) . '"' . $selected . '>' . esc_html($value) . esc_attr($after_text) . '</option>';
}
echo '</select>';
}
// 選択肢型カスタムフィールド用の関数
function add_choices_filter($meta_key,$label) {
$stock_choices = get_field_object($meta_key)['choices'];
$selected_value = filter_input(INPUT_GET, $meta_key);
echo '<select name="' . esc_attr($meta_key) . '" id="' . esc_attr($meta_key) . '">';
echo '<option value="">' . esc_html($label) . '</option>';
foreach ($stock_choices as $key => $value) {
$selected = selected($selected_value, $key, false);
echo '<option value="' . esc_attr($key) . '"' . $selected . '>' . esc_html($value) . '</option>';
}
echo '</select>';
}
/*----------------
* プルダウンで選ばれた項目で絞り込む機能を実装する
*----------------*/
add_action( 'pre_get_posts', 'pre_get_posts_admin' );
function pre_get_posts_admin( $query ) {
if ( is_admin() && $query->is_main_query() && 'post' === $query->get('post_type')) {
$inputs = [
'publisher' => filter_input( INPUT_GET, 'publisher'),
'price' => filter_input( INPUT_GET, 'price'),
'stock' => filter_input( INPUT_GET, 'stock')
];
$meta_query = $query->get('meta_query');
if(!is_array($meta_query)){
$meta_query = [ 'relation' => 'AND' ];
}
foreach($inputs as $key => $value){
if(!empty($value)){
$meta_query[] = [
'key' => $key,
'value' => $value,
'compare' => '='
];
}
}
if(count($meta_query) > 1){
$query->set('meta_query',$meta_query);
}
}
}
/*----------------
* デフォルトのプルダウンを非表示に
*----------------*/
add_filter('disable_months_dropdown','remove_dropdown',10,2);
add_filter('disable_categories_dropdown','remove_dropdown',10,2);
function remove_dropdown($disable,$post_type){
if($post_type === 'post'){
$disable = true;
}
return $disable;
}