是否可以完全停止WP_Query检索帖子?


8

我正在尝试使用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.
    }
});

问题

returnexit在这种情况下不起作用。然后,WP_Query仍然会命中数据库以再次检索帖子。

问题

无论使用哪种插件,是否都可以完全停止WP_Query检索帖子?


我觉得该插件应该可以处理此问题...您确定要使用正确的方法吗?您在他们的论坛上问过这个吗?在他们的github问题上?
Howdy_McGee

@Howdy_McGee插件使用与默认WordPress缓存API相同的功能。唯一的区别是它有助于连接到Redis服务器。当然,我也在努力寻找正确的方法。
SarahCoding '16

不知道为什么您认为该查询不会触发。从动作中返回不会从调用函数中神奇返回
Mark Kaplun

@MarkKaplun我也对此加倍,但这return可能是我们在这种情况下可以调用的唯一命令。
SarahCoding '16

@Dan,我不明白您的假设,您显然假设不正确,可能是在php级别
Mark Kaplun

Answers:


11

目前,这是不可能的。

'pre_get_posts'运行时,为时已晚停止WP_Query执行查询。

WordPress本身在您尝试查询不存在的分类法时会添加AND (0 = 1)WHERESQL查询的子句中,以确保它不会很快返回任何结果。

有一个带补丁的Trac票可能会在WP 4.6的核心中加入,它引入了一个新的过滤器:'posts_pre_query'。在该过滤器上返回数组将WP_Query停止处理,并使用提供的数组作为posts数组。

这可能会以某种方式帮助您实现您要执行的操作。

等一下,您可以做的任何事情都是hackhack的,内核本身使用的技巧也很hackhack。

最近,当我想停止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 Sumit

可以在WordPress的各个地方应用的有趣方法,我会牢记在心;-) ps: DTO =数据传输对象
birgire

@birgire yes :)
gmazzap

2

这是PHP问题,而不是WordPress问题。

正如@Mark所说:

从动作中返回,不要从调用函数中魔术返回

那是真实的。放置return在功能上平均排出的功能和放置在一个PHP文件平均排出文件返回。不要与PHP结构exit():P 混淆(您可能会在SO上找到关于PHP的更好答案return)。

并回答你的问题

您可以通过获取单列而不是完整表来减少查询的负担。就像@birgire在这里所做的一样删除主页查询

可能是一个更好的答案。我只是分享我所知道的:)


1
@Dan通过posts_request过滤器中和查询请求后,是否得到许多数据库点击?通过这种+单列方法,我们WP_Query比使用posts_pre_query过滤器更早退出。.还请注意带有的粘性帖子,posts_pre_query$q->set( 'ignore_sticky_posts', 1 );例如,可以在此处的示例中将其删除。
birgire

@birgire看起来posts_pre_query没有帮助。您的解决方案是迄今为止最好的。:)如果您知道之后如何退出查询pre_get_posts,那就太好了。谢谢!
SarahCoding

@Dan posts_pre_query将从4.6开始可用;)
Sumit

我想到的另一种方法是尝试使用可能早存在WP_Query的自定义get_posts()方法扩展该类,并使用该方法进行调用parent::get_posts() 并尝试覆盖相关查询。但我不知道这对您的情况是否
有用

1
也许有一点 Aerosmith-Livin'On The Edge可以帮上忙;-) @Dan
birgire


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/

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.