问题描述

有一个long-standing WordPress核心错误(#16373),其中如果自定义查询变量被注册并存在于查询字符串中,查询将不会设置is_front_page()为真,即使'page' == get_option( 'show_on_front' )。完整的详细信息在trac门票中,但最终结果是首页返回无效页面而不是get_option( 'page_on_front' )

我有一些用例,我需要将自定义查询变量传递到首页上的查询字符串(使用表单传递注册的查询变量,这些变量反过来用于过滤主题选项 – front-end主题选项演示)。 above-mentioned trac车票被关闭为无效,所以我需要一个work-around。

我可以轻松地强制前头模板,就像这样:

function themeslug_force_front_page_template( $template ) {
    if ( '' != get_query_var( 'foobar' ) ) { // Registered custom query var
        return get_front_page_template();
    }
    return $template;
}
add_filter( 'template_include', 'themeslug_force_front_page_template' );

但是,基础查询条件不受影响。因此,任何依赖于is_front_page()的代码都是true(例如,仅在站点首页上显示的滑块)仍然无法正常呈现。

我试图在pre_get_posts修改$query,但这似乎没有工作:

function themeslug_force_static_front_page( $query ) {
    if ( $query->is_main_query() ) {
        if ( 'page' == get_option( 'show_on_front' ) ) {
            if ( '' != get_query_var( 'foobar' ) ) { // Registered custom query var
                $query->set( 'page_id', get_option( 'page_on_front' ) );
                $query->set( 'is_home', false );
                $query->set( 'is_page', true );
                $query->set( 'is_front_page', true );
            }
        }
    }
}
add_action( 'pre_get_posts', 'themeslug_force_static_front_page' );

查询条件不被修改。我是否在我的回调中错误地尝试?是否有不同/更好的方式强制查询条件,特别是is_front_page()

Edit

请注意,设置page_id可以在pre_get_posts回调中工作:

$query->set( 'page_id', get_option( 'page_on_front' ) );

如果省略template_include过滤器,则显示的页面确实是分配给首页的页面;然而,使用get_page_template(),而不是get_front_page_template()。因此,在pre_get_posts中设置page_id确实会导致is_page()为真。但是上述的bug阻止了is_front_page()被设置为true

编辑2

Per @ toscho的请求,这里有一些查询var代码,用于上下文。

注册查询变量:

/**
 * Add options-related query variables
 */
function themslug_add_theme_demo_query_vars( $qvars ) {
    $qvars[] = 'demo_foo';
    $qvars[] = 'demo_bar';
    $qvars[] = 'demo_baz';
    return $qvars;
}
add_filter( 'query_vars', 'themeslug_add_theme_demo_query_vars' );

(foobarbaz是主题选项 – 背景,各种颜色等)

如何使用它们只是为主题选项添加一个过滤器 – 该部分超出了问题的范围,因此我将在此省略。

它们通过自定义Widget在前端输出。小工具输出只是一个表单。以下是一个表单域的示例:

<h3>Foo</h3>
<input name="demo_foo" id="demo_foo" class="pickcolor"  type="text" value="<?php echo $foo_setting; ?>" data-default-color="<?php echo $foo_setting; ?>" />

生成的查询字符串on-submit:

www.example.com/?foo=some_value

最佳解决方案

你检查了is_front_page()的哪一部分导致它返回false?

我可以通过跟踪从trac机票的设置重现问题。在我的这个函数内,调用is_page()返回false

我猜这是因为$wp_query->set()用于page_id,而is_page只是导致query_vars被改变,你不会改变“查询状态”本身。

我可以通过将这两行添加到您的代码来创建一个解决方法:

$query->is_page = true;
$query->queried_object = get_post(get_option('page_on_front') );

在页面正确显示(在我的设置中)导致的页面(尽管URL中存在query_var),还有is_front_page()返回true

我希望这有助于你进一步。


如果您查看WP_Query类的定义,您将看到有一个变量$query_vars和一个变量$is_page

您使用的set功能($query->set( 'is_page', true );)仅设置query var

function set($query_var, $value) {
    $this->query_vars[$query_var] = $value;
}

它不会更改保存在$query->is_page = true中的状态信息,而是设置查询var $query->query_vars['is_page'] = true

如果您来自OOP-approach并将WP_Query::set()理解为经典的class-setter功能,那么这是有点误导的 – 它不是。

次佳解决方案

我也在那张Trac票里回答,但是我认为这里的基本问题比这更简单。

你正在挂钩在demo_foo,demo_bar,demo_baz作为”Query variables”,但他们不是;至少不是在意图上。

虽然URL后的foo = bar字符串通常称为查询字符串,但”query variables”实际上只是影响主查询的变量。如果你不挂钩一个变量,它不会消失,它不会被主要的WP_Query对象读取。

在你的情况下,这似乎正是你想要的。您的变量是主题选项,它们不是主要查询(它从数据库获取帖子)的一部分。您没有使用它们来选择要显示的帖子,您正在使用它们来设置主题选项等。

如果你不把它们挂接成query_vars,那么它们根本不会影响主WP_Query对象。您将不得不从主要的$ _GET超级全局获取它们,而不是使用get_query_var,您将不得不对其进行适当的清理,以防止反射性XSS攻击(使用esc_attr或任何适当的),但这应该通过消除它来解决您的主要问题该代码首先引起问题,而不是添加额外的解决方法。

参考文献

注:本文内容整合自Google/Baidu/Bing辅助翻译的英文资料结果。如果您对结果不满意,可以加入我们改善翻译效果:薇晓朵技术论坛。