問題描述

有一個 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 輔助翻譯的英文資料結果。如果您對結果不滿意,可以加入我們改善翻譯效果:薇曉朵技術論壇。