WordPressで投稿クエリを柔軟に制御する方法

posts_* 系フィルター8選と実用的なカスタマイズ例


WordPress の WP_Query は便利なAPIですが、標準機能だけでは対応できない場面が少なくありません。

たとえば:

  • カスタムフィールドの値で並べ替えたい
  • 投稿タイトルとカスタムフィールドの両方を検索対象にしたい
  • JOINによる重複投稿を排除したい
  • 完成するSQLを確認・編集したい

こういったニーズを実現するには、posts_* 系のフィルターフックを使ったSQLレベルの制御が非常に効果的です。

この記事では、代表的な8つの posts_* フィルターの用途・使い方・実行されるSQL・注意点を解説し、実用的なカスタマイズパターンまでご紹介します。


フィルター一覧と概要

フィルター名操作対象主な用途
posts_fieldsSELECT句取得するカラムの制御
posts_joinJOIN句postmeta などとの結合
posts_whereWHERE句検索条件の追加
posts_groupbyGROUP BY句投稿単位での集約
posts_orderbyORDER BY句並び順の制御
posts_distinctSELECTのDISTINCT重複の除去
posts_clauses全体まとめて一括でJOIN/WHERE/ORDERを制御
posts_request最終SQL全体デバッグ・ログ出力用

各フィルターの用途と実行されるSQL

1. posts_fields

add_filter('posts_fields', function($fields, $query) {
    if ($query->is_main_query() && $query->is_search()) {
        // 必要なカラムだけ取得(最小化)
        return 'DISTINCT wp_posts.ID, wp_posts.post_title';
    }
    return $fields;
}, 10, 2);

SQL例:

SELECT DISTINCT wp_posts.ID, wp_posts.post_title

2. posts_join

add_filter('posts_join', function($join) {
    global $wpdb;
    if (is_search()) {
        // 全てのカスタムフィールドをJOIN
        $join .= " LEFT JOIN {$wpdb->postmeta} ON {$wpdb->posts}.ID = {$wpdb->postmeta}.post_id ";

        // 並び順用に kana フィールドを別名でJOIN(エイリアス)
        $join .= " LEFT JOIN {$wpdb->postmeta} AS kana_meta
                   ON {$wpdb->posts}.ID = kana_meta.post_id
                   AND kana_meta.meta_key = 'kana' ";
    }
    return $join;
});

SQL例:

LEFT JOIN wp_postmeta ON wp_posts.ID = wp_postmeta.post_id
LEFT JOIN wp_postmeta AS kana_meta ON wp_posts.ID = kana_meta.post_id AND kana_meta.meta_key = 'kana'

3. posts_where

add_filter('posts_where', function($where) {
    global $wpdb;
    if (is_search()) {
        $where = preg_replace(
            "/\(\s*{$wpdb->posts}\.post_title\s+LIKE\s*(\'[^\']+\')\s*\)/",
            "({$wpdb->posts}.post_title LIKE $1) OR ({$wpdb->postmeta}.meta_value LIKE $1)",
            $where
        );
    }
    return $where;
});

SQL例:

WHERE (wp_posts.post_title LIKE '%検索語%')
   OR (wp_postmeta.meta_value LIKE '%検索語%')

4. posts_groupby

add_filter('posts_groupby', function($groupby, $query) {
    global $wpdb;
    if ($query->is_main_query() && $query->is_search()) {
        return "{$wpdb->posts}.ID";
    }
    return $groupby;
}, 10, 2);

SQL例:

GROUP BY wp_posts.ID

5. posts_orderby

add_filter('posts_orderby', function($orderby, $query) {
    global $wpdb;
    if ($query->is_main_query() && $query->is_search()) {
        // kana_meta は JOIN 時に定義したエイリアス
        return "kana_meta.meta_value ASC, {$wpdb->posts}.post_date DESC";
    }
    return $orderby;
}, 10, 2);

SQL例:

ORDER BY kana_meta.meta_value ASC, wp_posts.post_date DESC

6. posts_distinct

add_filter('posts_distinct', function($distinct) {
    if (is_search()) {
        return 'DISTINCT';
    }
    return $distinct;
});

SQL例:

SELECT DISTINCT wp_posts.*

7. posts_clauses

add_filter('posts_clauses', function($clauses, $query) {
    global $wpdb;
    if ($query->is_main_query() && $query->is_search()) {
        $clauses['join'] .= " LEFT JOIN {$wpdb->postmeta} AS kana_meta 
                              ON {$wpdb->posts}.ID = kana_meta.post_id 
                              AND kana_meta.meta_key = 'kana'";
        $clauses['orderby'] = "kana_meta.meta_value ASC";
    }
    return $clauses;
}, 10, 2);

8. posts_request

add_filter('posts_request', function($sql, $query) {
    if ($query->is_main_query() && $query->is_search()) {
        error_log('[SQL LOG] ' . $sql); // ログファイルへ出力
    }
    return $sql;
}, 10, 2);

応用的な実装パターン

パターン1:投稿タイトル+カスタムフィールドで検索し、kana で並び替える

  • posts_joinpostmeta + kana_meta をJOIN
  • posts_wheremeta_valueも検索対象に
  • posts_orderbykana_meta.meta_value でソート
  • posts_distinct:重複排除
  • posts_groupbyIDでまとめる(必要に応じて)

このように複数のフックを組み合わせて制御することがベストプラクティスです。


パターン2:meta_queryを使った検索とはどう違う?

WordPress標準の meta_query を使うと、AND/OR 条件を指定してカスタムフィールドを検索できますが、
「検索キーワードとカスタムフィールドの両方を対象にする」「meta_keyを柔軟に制御する」といったケースにはposts_where のようなSQLレベルの調整が適しています。


おわりに

WordPressの WP_Query は、柔軟性が高い一方で、標準APIだけでは限界があるシナリオも多く存在します。
今回ご紹介した posts_* 系のフィルターを活用することで、SQLの構造を理解しながら、本格的な検索・並び順のカスタマイズが実現可能になります。

コメント

タイトルとURLをコピーしました