web-dev-qa-db-ja.com

WP_Queryによる投稿の取得を完全に停止することはできますか?

私は WP Redis を使って $ wp_query オブジェクト全体を $ query_vars_hash とキャッシュしようとしています。

これが$wp_query$wp_object_cacheに追加された方法です。

add_action('wp', function($wp)
{
    if ( is_admin() ) return;

    global $wp_query;

    if ( !wp_cache_get($wp_query->query_vars_hash, 'globals') )
    {
        wp_cache_add($wp_query->query_vars_hash, $wp_query, 'globals');
    }
});

WP_Queryが投稿を取得する前に、クエリが既にキャッシュされているかどうかを確認する必要があります。

add_action('pre_get_posts', function($query)
{
    if ( is_admin() ) return;

    $cached_query = wp_cache_get($query->query_vars_hash, 'globals');

    if ($cached_query)
    {
        $GLOBALS['wp_query'] = &$cached_query;

        return; // Return immediately to prevent retrieving posts again.
    }
});

問題

この場合、returnまたはexitは機能しません。それから、WP_Queryはまだ投稿を検索するためにデータベースにヒットします。

質問

プラグインに関係なく、投稿の取得をWP_Queryで完全に停止することは可能ですか?

8
MinhTri

現時点では、それは不可能です。

'pre_get_posts'が実行されるとき、クエリを実行するためにWP_Queryを停止するには遅すぎます。

WordPress自体が、存在しない分類法を照会しようとすると、SQL照会のWHERE節にAND (0 = 1)を追加して、結果が非​​常に速く返されないようにします。

tracチケット はWP 4.6のコアとなると思われるパッチがあります。これは新しいフィルタを導入します:'posts_pre_query'。そのフィルタに配列を返すと、WP_Queryは処理を停止し、提供された配列をposts配列として使用します。

これはどういうわけかあなたがやろうとしていることを実行するのにあなたを助けるかもしれません。

これを待って、あなたができることは何とかして ハッキング トリック コア自体が同様にかなりハックなことです。

最近、私は トリック を使い始めて、きれいに止めることができないことをWordPressでやめたいと思っています。

例を示します。ここにあるすべてのコードは完全にテストされていないことに注意してください。

まず最初に、カスタム例外を書きましょう。

class My_StopWpQueryException extends Exception {

   private $query;

   public static forQuery(WP_Query $query) {
     $instance = new static();
     $instance->query = $query;

     return $instance;
   }

   public function wpQuery() {
     return $this->query;
   }
}

この例外は、クエリオブジェクトを転送するための一種の _ dto _ として機能するように設計されているため、catchブロックで取得して使用できます。

より良いコードで説明してください:

function maybe_cached_query(WP_Query $query) {
    $cached_query = wp_cache_get($query->query_vars_hash, 'globals');
    if ($cached_query instanceof WP_Query)
       throw My_StopWpQueryException::forQuery($cached_query);
}

function cached_query_set(WP_Query $query) {
    $GLOBALS['wp_query'] = $query;
    $GLOBALS['wp_the_query'] = $query;
    // maybe some more fine-tuning here...
}

add_action('pre_get_posts', function(WP_Query $query) {
    if ($query->is_main_query() && ! is_admin()) {
        try {
           maybe_cached_query($query);
        } catch(My_StopWpQueryException $e) {
           cached_query_set($e->wpQuery());
        }
    }
});

多かれ少なかれ動作するはずですが、起動しないフックがたくさんあります。例えば"the_posts"など。これらのフックのいずれかを使ってトリガーするコードがあると、壊れることがあります。

テーマ/プラグインが必要とするかもしれないフックのいくつかを起動するためにcached_query_set関数を使うことができます。

11
gmazzap

これはWordPressの質問よりもPHPの質問です。

As @Mark さんがコメントしました:

アクションから戻ってきても、呼び出し元の関数から魔法で戻ってきません

それは本当です。 returnをfunctionに入れることは関数を終了することを意味し、returnをPHP fileに置くことはファイルを終了することを意味します。 PHPコンストラクトexit():Pと混同しないでください(SO returnについてはPHPのほうが良い答えが見つかるかもしれません)。

そしてあなたの質問に答えるために

フルテーブルではなく単一のカラムを取得することで、クエリの負荷を軽減できます。 Like @birgire did here ホームページのクエリを削除する

まだ良い答えになるかもしれません。私はちょうど私が知っていることを共有しました:)

2
Sumit

それは4.6でリリースされるまで変更されていないと仮定されます(posts_pre_query filter https://core.trac.wordpress.org/ticket/36687

2
Mark Kaplun

うん、キャッシュしたいものによっては可能です。私たちのホームページにメインループをキャッシュするために私は同様のことをしました。基本的には、posts_requestposts_resultsを使ってクエリをハイジャックし、代わりにキャッシュをヒットしてから、found_postsを使ってページネーションを修正することができます。

非常に大まかな例は私たちのコードから引き出された(未テスト)が、あなたはあなたがアイデアを得るのを手伝うべきである:

<?php
/**
 * Kill the query if we have the result in the cache
 * @var [type]
 */
add_filter( 'posts_request', function( $request, $query ) {
    if ( is_home() && $query->is_main_query() ) {

        $page = ( get_query_var( 'paged' ) ) ? get_query_var( 'paged' ) : 1;

        $key = 'homepage_query_cache_' . $page;

        if ( wp_cache_get( $key, 'cache_group' ) )
            $request = null;

    }

    return $request;
}, 10, 2 );

/**
 * Get the result from the cache and set it as the query result
 * Or add the query result to the cache if it's not there
 * @var [type]
 */
add_filter( 'posts_results', function( $posts, $query ) {

    if ( is_home() && $query->is_main_query() ) {

        $page = ( get_query_var( 'paged' ) ) ? get_query_var( 'paged' ) : 1;

        $key = 'homepage_query_cache_' . $page;

        if ( $cached_posts = wp_cache_get( $key, 'cache_group' ) ) {
            $posts = $cached_posts;
        } else {
            wp_cache_set( $key . '_found_posts', $query->found_posts, 'cache_group', HOUR_IN_SECONDS );
            wp_cache_set( $key, $posts, 'cache_group', HOUR_IN_SECONDS );
        }
    }

    return $posts;

}, 10, 2 );

/**
 * Correct the found posts number if we've hijacked the query results
 * @var [type]
 */
add_filter( 'found_posts', function( $num, $query ) {
    if ( is_home() && $query->is_main_query() ) {
        $page = ( get_query_var( 'paged' ) ) ? get_query_var( 'paged' ) : 1;

        $key = 'homepage_query_cache_' . $page;

        if ( $found_posts = wp_cache_get( $key . '_found_posts', 'cache_group' ) )
            $num = $found_posts;
    }

    return $num;
}, 10, 2 );

もっとここに: https://www.reddit.com/r/Wordpress/comments/19crcn/best_practice_for_hijacking_main_loop_and_caching/

2
OzTheGreat