投稿を取得するWP_Queryを完全に停止することは可能ですか?


8

WP Redisを使用して、キーが$ query_vars_hashの$ wp_queryオブジェクト全体をキャッシュしようとしています。

これは以下$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投稿の取得を完全に停止することは可能ですか?


プラグインがこれを処理する必要があると思います...これを正しい方法で行っていますか?彼らのフォーラムでこれについて尋ねましたか?彼らのgithubの問題について?
Howdy_McGee

@Howdy_McGeeプラグインは、デフォルトのWordPressキャッシングAPIと同じ機能を使用します。唯一の違いは、Redisサーバーへの接続に役立ちます。もちろん、私も正しい方法を見つけようとしています。
SarahCoding 2016年

クエリが起動しないと思う理由はわかりません。アクションから戻ると、呼び出し元の関数から魔法で戻ることはありません
Mark Kaplun

@MarkKaplun私もそれについて倍増しましたがreturn、この場合に呼び出すことができる唯一のコマンドかもしれません。
SarahCoding 2016年

@ダン、私はあなたが想定していることを理解していません。おそらくphpレベルで、正しくないことを明らかに想定しています
Mark Kaplun

回答:


11

現時点では不可能です。

ときに'pre_get_posts'実行するには、遅すぎる停止するにはWP_Query、クエリを実行します。

WordPress自体、存在しない分類法をクエリしようとすると、SQLクエリの句に追加さAND (0 = 1)れ、WHERE結果がすぐに返されないようにします...

新しいフィルターを導入するパッチがWP 4.6のコアに含まれる可能性のあるパッチを含むtracチケットがあります'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関数を使用して、テーマ/プラグインに必要なフックの一部を起動できます。


デフォルトの例外クラスで動作しないのはなぜですか?キャッチされない例外のエラーが表示されますか?
Sumit

これは、標準の例外と公共の財産で動作するはずですが、あなたは、あなたがそれを@Sumitをスローする場合、標準の例外をキャッチする必要があります
gmazzap

さて、私はこの例を使用するだけでそれを行いました。しかし、キャッチされない例外エラーが発生します。私は例外の最も単純な例を実行しましたdo_actionが、tryブロックの中にあるようです。
Sumit

WordPressのさまざまな場所で適用できる興味深いアプローチです。覚えておきます;-) ps: DTO = Data Transfer Object
バージィ

@birgire yes :)
gmazzap

2

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

同様に@マークがコメント:

アクションから戻ると、呼び出し元の関数から魔法で戻らない

それは本当です。配置return機能平均の出口に機能をし、PHPファイルの平均退出ファイル内のリターンを配置します。PHPのコンストラクトexit():P と混同しないでください(PHPについてSOでもっと良い答えが見つかるかもしれませんreturn)。

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

テーブル全体ではなく単一の列をフェッチすることにより、クエリの負荷を軽減できます。@birgireがここでやったように、ホームページのクエリを削除します

まだこれからの方が良い答えかもしれません。私は知っていることを共有しました:)


1
@Danは、posts_requestフィルターを介してクエリ要求を無効にした後、多くのデータベースヒットを受け取りましたか?その+シングルカラムアプローチではWP_Queryposts_pre_queryフィルターを使用する前に終了します。また、粘着性のある投稿に注意してくださいposts_pre_queryただし$q->set( 'ignore_sticky_posts', 1 );ここの例で削除できます
バージィ

@birgire Look like posts_pre_queryは役に立ちません。あなたの解決策は今のところ最高です。:)直後にクエリを終了する方法を知っている場合pre_get_posts、それは素晴らしいことです。ありがとうございました!
SarahCoding

@Dan posts_pre_queryは4.6以降で利用可能になります;)
Sumit

頭に浮かぶもう1つのアプローチは、可能な限り早期に存在する可能性WP_Queryがあるカスタムget_posts()メソッドを使用してクラスを拡張しparent::get_posts() 、関連するクエリを呼び出してオーバーライドすることです。しかし、それがうまくいくか、ここであなたのケースで意味をなすかどうかはわかりません;-) @Dan
バージリー

1
多分エアロスミスの少し-Livin 'On The Edgeがそれを助けるかもしれません ;-) @Dan
バージリー

2

新しいposts_pre_queryフィルターhttps://core.trac.wordpress.org/ticket/36687を使用して、4.6で(リリースまで変更がないと仮定して)可能になります


@ダン、これは、寝る前に考えていたものを最初に完了し、他の回答を先に読まないようにしたいときに起こります;)
Mark Kaplun

ブロさん、もう遅すぎます。私は後でそれらの回答を読みます;-)
SarahCoding

2

ええ、キャッシュしたいものに応じて可能です。同様のことを行って、メインループをホームページにキャッシュしました。基本的に、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/

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.