web-dev-qa-db-ja.com

WP_Queryに結果を返さないように強制できますか?

私は、ユーザーが多くの投稿メタを検索できるようにする検索機能を備えたWebサイトに取り組んでいます。強制的に結果を返さないようにしたい特定の検索パターンがあります。 WP_Queryは技術的にはデータベースで結果を見つけるでしょうが、どうにかしてそれをオーバーライドしてif( $example->have_posts() )を失敗させるために結果を返さないよう強制したいのです。

'force_no_results' => trueのように、結果を返さないように強制するような、WP_Queryに渡すことができるある種のパラメーターはありますか?

21
Brian

やってみる

'post__in' => array(0)

シンプルで要点に。

26
David Labbe

奇妙なことに、WP_Queryをショートさせる明確で明確な方法はありません。

もしそれが main queryでWP->parse_request()の周りに何か問題があるかもしれないなら、比較的最近の(3.5)do_parse_requestフィルタがあるようです。

しかし、WP_Query自体には、AND 1=0フィルターを介したposts_whereの追加によるSQLクエリの短絡など、通常ダーティーハックが秩序だっています。

3
Rarst

クエリパラメータを存在しない値に設定する際の問題は2です。

  • クエリが実行されるため、すでに結果が得られないことがわかっている場合でも、支払うべきパフォーマンスの値段はわずかです
  • WordPressクエリには、クエリに作用する19種類の'posts_*'フィルタフック('posts_where''post_join'など)があるため、クエリに存在しないparamを設定しても結果が返されないこと、フィルタによって返される単純なOR句が何かを返すことを確認できません。 。

確実 /クエリが結果を返さず、パフォーマンス上の問題がまったくない(または最小限になる)ためには、少し{hardcoreルーチンが必要です。

そのルーチンを起動するには、すべてのメソッドを使用できます。技術的には、存在しないイベント引数WP_Queryに任意の引数を渡すことができます。

あなたが'force_no_results' => trueのようなものが好きなら、あなたはそれをそのように使うことができます:

$a = new WP_Query( array( 's' => 'foo', 'force_no_results' => true ) );

そして'pre_get_posts'上で実行されるコールバックを追加します。

add_action( 'pre_get_posts', function( $q ) {
  if (array_key_exists('force_no_results', $q->query) && $q->query['force_no_results']) {
    $q->query = $q->query_vars = array();
    $added = array();
    $filters = array(
      'where', 'where_paged', 'join', 'join_paged', 'groupby', 'orderby', 'distinct',
      'limits', 'fields', 'request', 'clauses', 'where_request', 'groupby_request',
      'join_request', 'orderby_request', 'distinct_request','fields_request',
      'limits_request', 'clauses_request'
    );
    // remove all possible interfering filter and save for later restore
    foreach ( $filters as $f ) {
      if ( isset($GLOBALS['wp_filter']["posts_{$f}"]) ) {
        $added["posts_{$f}"] = $GLOBALS['wp_filter']["posts_{$f}"];
        unset($GLOBALS['wp_filter']["posts_{$f}"]);
      }
    }
    // be sure filters are not suppressed
    $q->set( 'suppress_filters', FALSE );
    $done = 0;
    // use a filter to return a non-sense request
    add_filter('posts_request', function( $r ) use( &$done ) {
      if ( $done === 0 ) { $done = 1;
        $r = "SELECT ID FROM {$GLOBALS['wpdb']->posts} WHERE 0 = 1";
      }
      return $r;
    });
    // restore any filter that was added and we removed
    add_filter('posts_results', function( $posts ) use( &$done, $added ) {
      if ( $done === 1 ) { $done = 2;
        foreach ( $added as $hook => $filters ) {
          $GLOBALS['wp_filter'][$hook] = $filters;
        }
      }
      return $posts;
    });
  }
}, PHP_INT_MAX );

このコードが行うことは、できるだけ遅く'pre_get_posts'で実行されます。引数 'force_no_results'がクエリに存在する場合は、次のようになります。

  1. まず、クエリに干渉する可能性のあるすべてのフィルタを削除し、それらをヘルパー配列に格納します。
  2. SELECT ID FROM wp_posts WHERE 0 = 1すべてのフィルタが削除されると、このクエリが変更される可能性はなく、非常に高速であり、確実な結果は得られません。
  3. このクエリが実行された直後に、すべての元のフィルタ(存在する場合)が復元され、以降のすべてのクエリは期待どおりに機能します。
2
gmazzap