問題描述

當從類別頁面或任何頁面點選單個帖子時,您可以獲取該引用來源的 URL 並解析它以獲取查詢字串。但這隻適用於預設的永久連結結構

例如,引薦來源網址是類別頁面:

var_dump( parse_url( wp_get_referer() ) ); 使用預設的 permalink 結構提供以下輸出

array(4) {
  ["scheme"]=>
  string(4) "http"
  ["host"]=>
  string(9) "localhost"
  ["path"]=>
  string(11) "/wordpress/"
  ["query"]=>
  string(5) "cat=7"
}

使用相同的 var_dump()與固定連結設定為/%postname%/,這是你得到的

array(3) {
  ["scheme"]=>
  string(4) "http"
  ["host"]=>
  string(9) "localhost"
  ["path"]=>
  string(32) "/wordpress/category/uit-my-lewe/"
}

我可以使用 path 從第二個程式碼塊與 get_category_by_path(),我可以得到類別物件

我的問題是,我如何去分類術語。

我做了一個測試。我有一個自定義分類 event_cat 。如果我重寫 event-slug,我得到以下 path 使用/%postname%/作為永久連結結構

/wordpress/event-slug/5-star/

event_cat=5-star

使用預設結構

自動地,我的分類名稱不會在 URL 中,只是我的術語。所以,這不是一個非常失敗的安全方法,從這個術語中獲取物件。

我的問題是,如何正確獲取預設的永久連結結構獲取查詢字串,或查詢字串或/%postname%/永久連結結構中的分類和術語名稱

最佳解決方案

首先我不得不說,wp_get_referer()不是 100%可靠,因為它依賴於 $_SERVER['HTTP_REFERER']不是 100%可靠,從 php.net 檔案:

The address of the page (if any) which referred the user agent to the current page. This is set by the user agent. Not all user agents will set this, and some provide the ability to modify HTTP_REFERER as a feature. In short, it cannot really be trusted.

替代方案

如果您可以新增釋出網址一個查詢引數,說明帖子來自哪裡,它將更可靠,您不需要解析一個 url 來獲取一個術語物件。

例:

add_filter('post_link', function($permalink) {
  if (is_category() && ($cat = get_queried_object())) {
    $permalink = esc_url(add_query_arg(array('catfrom' => $cat->term_id), $permalink));
  }
  return $permalink;
});

這樣做,從類別頁面點選的固定連結將會傳送給您的網址

http://example.com/wordpress/post-name?catfrom=12

並且您可以輕鬆瞭解使用者來自哪裡,而不依賴於 $_SERVER['HTTP_REFERER'],無需任何其他努力。

回答你的問題

從網址獲取查詢資訊是 WordPress 在 WP::parse_request()方法中所做的。

該方法僅用於”main” url(使用者正在檢視的 url),而不是任意 URL 。

幾個月前,我寫了 Url To Query 外掛,目的是為任意 URL 執行相同的操作。

我做的是使用 WP::parse_request(),並將其重構為一個更加準確的 OOP 程式碼,並使其與任意 URL 一起工作 (例如,作為引數的 url 被收到,而不是從 $_SERVER var 獲取) 。

可以使用我的外掛

$args = url_to_query('/wordpress/event-slug/5-star/');

var_dump($args); // array( 'event_slug' => '5-star' );

所以你從 url 開始獲取查詢引數 (你可以直接傳遞給 new WP_Query),這正是 WP::parse_request()所做的。

在你的情況下,您可以檢查 args 陣列,而不需要實際執行查詢。

這可以肯定地起作用,但是我認為分析 URL 和 $_SERVER['HTTP_REFERER']的不可靠性需要額外的努力,使得第一個解決方案更適合您的範圍。

次佳解決方案

這個問題的初衷是要知道哪一個職位是從哪裡提交的,然後根據這個職位,根據職位介紹人提供下一個職位和以前的職位。

例如我想要完成的

從類別,分類標籤,標籤,搜尋或作者歸檔頁面點選了一個帖子。這些檔案作為引薦來源。現在,正如我在問題中,wp_get_referer()通常會使用這個引用來進行進一步的查詢。如 @G.M. in his accepted answer above 所述,這種方法是不可靠的,所以我去使用他的替代解決方案。

另一個問題是需要使用某種 cookie 或會話來儲存此引薦來源,以便當您從特定歸檔中點選的原始單個帖子導航時,您仍然可以從原始引薦來源列表中刪除帖子。由於 Cookie 也由終端使用者控制,因此不可靠,以及 WordPress 預設情況下不使用會話的事實,我使用 @ G.M 重構了下一個和以前的帖子連結。具有可靠的方式來檢查和儲存我的原始引薦來源的替代解決方案。

這是我想出來的,我希望有人會在不久的將來發現它有用。請使用和濫用程式碼以滿足您的需求,只需一個請求:將連結返回到此問題。 🙂

關於以下程式碼的註釋

  • 這段程式碼是相當長的和密集的,所以我不會詳細介紹。程式碼已被很好地評論

  • 該程式碼可以選擇在同一術語中的帖子之間進行頁面訪問,就像 WordPress 中的預設 next_post_link()previous_post_link()函式一樣。就像本機功能一樣,您必須設定分類。 in_same_term 的預設值為 true,分類為 category

  • 最重要的是,這段程式碼需要 PHP 5.4+

程式碼

<?php
/**
 * @author Pieter Goosen
 * @license GPLv2
 * @link http://www.gnu.org/licenses/gpl-2.0.html
 *
 * The functions on this page returns the next and previous post links
 * depending on what has been set
 *
 * @return function single_post_navigation()
*/

/**
 * Register six new query variables aq, ,cq, tq, ttq, taq, and sq set by
 * the term_referer_link function
 *
 * @see http://codex.wordpress.org/WordPress_Query_Vars
 *
*/
add_filter( 'query_vars', function ( $vars ) {

    $vars[] = 'cq'; // Will hold category ID
    $vars[] = 'tq'; // Will hold taxonomy name
    $vars[] = 'ttq'; // Will hold term slug
    $vars[] = 'sq'; // Will hold search query
    $vars[] = 'aq'; // Will hold author name
    $vars[] = 'taq'; // Will hold tag id


    return $vars;

}, 10, 3 );

/**
 * Conditional tag to check whether or not a query_var has been set
 *
 * @param string $query_var query_var to check
 * @return (bool) true if query_var exists, false on failure
 *
*/
function has_query_var( $query_var ) {

    $array = $GLOBALS['wp_query']->query_vars;

    return array_key_exists( $query_var, $array );

}

/**
 * For posts being clicked from a category page, the query_var, 'cq' is set.
 * 'cq' holds the category ID
 *
 * Set two query_var, 'tq' and 'ttq' to single posts that was clicked on from
 * taxonomy pages. 'tq' holds the taxonomy name while 'ttq' holds the term name
 *
 * For search queries, the query_var, 'sq' is set to single posts that was clicked on from
 * the search page. 'sq' holds the search query value
 *
 * For posts being clicked from an author page, the query_var, 'aq' is set.
 * 'aq' holds the author ID
 *
 * For posts being clicked from a tag page, the query_var, 'taq' is set.
 * 'taq' holds the tag ID
 *
 * This function replaces the wp_get_referer() and $_SERVER['HTTP_REFERER']
 * functions that are not very reliable
 * @see php.net manual $_SERVER['HTTP_REFERER']
 * @link http://php.net/manual/en/reserved.variables.server.php
 *
 * @uses add_query_arg()
 * @uses post_link
 * @uses post_type_link
 *
*/
add_filter( 'post_type_link', 'term_referer_link', 10, 3 );
add_filter( 'post_link', 'term_referer_link', 10, 3 );

function term_referer_link( $permalink, $post ) {

    switch ( true ) {

        case ( is_category() ):

            $category = get_queried_object_id();

            $args = [
                'cq'    => $category,
            ];

            break;
        case ( is_tax() ):

            $term = get_queried_object();

            $args = [
                'tq'    => $term->taxonomy,
                'ttq'   => $term->slug
            ];

            break;

        case ( is_search() ):

            $search = get_search_query();

            $args = [
                'sq'    => $search,
            ];

            break;

        case ( is_author() ):

            $author = get_queried_object_id();

            $args = [
                'aq'    => $author,
            ];

            break;

        case ( is_tag() ):

            $tag = get_queried_object_id();

            $args = [
                'taq'   => $tag,
            ];

            break;

    }

    if( isset( $args ) ) {

        $permalink  = add_query_arg( $args, $permalink );

    }

    return $permalink;

}

/**
 * @access private
 * This function is marked private and should not be used in any other functions
 *
 * This is a helper function for the main navigation function
 *
 * This function checks if any of the query variables is set in the single
 * post page URL. If they exist, the values are retrieved that were set
 * by the query variables
 *
 * These query variables are converted into query arguments for the query that will
 * be used to determine the current post position and the posts adjacent to the
 * current post which will translate in the next and previous post.
 *
 * When no query variables are present, an empty array of arguments is returned
 *
 * @uses has_query_var()
 * @return (array) $add_query_args_to_args Query variable to determine the next/previous post links
 * @see http://codex.wordpress.org/Function_Reference/add_query_arg
 *
*/
function _query_vars_to_query_args() {

    switch ( true ) {

        case ( has_query_var( 'cq' ) ): // For category referrer

            $category = get_query_var( 'cq' );

            $add_query_args_to_args = [
                'cat' => $category,
            ];

            break;

        case ( has_query_var( 'tq' ) && has_query_var( 'ttq' ) ): // For taxonomy term referrer

            $taxonomy   = get_query_var( 'tq' );
            $term       = get_query_var( 'ttq' );

            $add_query_args_to_args = [
                'tax_query' => [
                    [
                        'taxonomy'          => $taxonomy,
                        'field'             => 'slug',
                        'terms'             => $term,
                        'include_children'  => false,
                    ],
                ],
            ];

            break;

        case ( has_query_var( 'sq' ) ): // For search referrer

            $search = get_query_var( 'sq' );

            $add_query_args_to_args = [
                's' => $search,
            ];

            break;

        case ( has_query_var( 'aq' ) ): // For author referrer

            $author = get_query_var( 'aq' );

            $add_query_args_to_args = [
                'author' => $author,
            ];

            break;

        case ( has_query_var( 'taq' ) ): // For tag referrer

            $tag = get_query_var( 'taq' );

            $add_query_args_to_args = [
                'tag_id' => $tag,
            ];

            break;

        default: // Default: returns empty array on any other archive or homepage

            $add_query_args_to_args = [];

            break;

    }

    return $add_query_args_to_args;

}
/**
 * @access private
 * This function is marked private and should not be used in any other functions
 *
 * This is a helper function for the main pagination function. This function
 * checks if the defined query variables has been set in the URL of a single
 * post
 *
 * If any of the query variables are found on any given single post page, then
 * these query variables will be set to the next and previous post links according
 * to the single post's query variables
 *
 * This way, next and previous posts will be shown from the same category, term,
 * search query or author archive from which the original single post was referred
 * from.
 *
 * If a single post was referred from any other archive or main page, these query
 * variables will not be set, and function will default to an empty array and no
 * query variables will be set to the next and previous post links
 *
 * @uses has_query_var()
 * @return (array) $qv Query variable to add to next/previous post links
 * @see http://codex.wordpress.org/Function_Reference/add_query_arg
 *
 * @todo Other archives can be added later
*/
function _add_query_vars_to_nav_links() {

    switch ( true ) {

        case ( has_query_var( 'cq' ) ): // For category referrer

            $category = get_query_var( 'cq' );

            $qv = [
                'cq'    => $category,
            ];

            break;

        case ( has_query_var( 'tq' ) && has_query_var( 'ttq' ) ): // For taxonomy term referrer

            $taxonomy   = get_query_var( 'tq' );
            $term       = get_query_var( 'ttq' );

            $qv = [
                'tq'    => $term->taxonomy,
                'ttq'   => $term->slug
            ];

            break;

        case ( has_query_var( 'sq' ) ): // For search referrer

            $search = get_query_var( 'sq' );

            $qv = [
                'sq'    => $search,
            ];

            break;

        case ( has_query_var( 'aq' ) ): // For author referrer

            $author = get_query_var( 'aq' );

            $qv = [
                'aq'    => $author,
            ];

            break;

        case ( has_query_var( 'taq' ) ): // For tag referrer

            $tag = get_query_var( 'taq' );

            $qv = [
                'taq'   => $tag,
            ];

            break;


        default: // Default: returns empty array on any other archive or homepage

            $qv = [];

            break;

    }

    return $qv;

}

/**
 * This function returns navigation links to the next/previous single post
 * There are choices to which taxonomy to use, and whether adjacent posts should
 * be of the same term or not
 *
 * When in_same_term is set to true, you have a choice to use the parent term or
 * child term if a post belongs to both. If the parent term is not available, the child term
 * is automatically used
 *
 * @param array $defaults An array of key => value arguments. Defaults below
 * - bool in_same_term       Whether or not next/previous post should be in the same term Default true
 * - bool parent_term        If in_same_term is true, should the parent or child terms be used Default true
 * - string/array taxonomy   The taxonomy from which terms to use Default category
 * - string/array post_types Post types to get posts from. Uses current post's post type on empty string. Default empty string
 * - string previous_text    Text to display with previous post Default 'Previous post'
 * - string next_text        Text to display with next post Default 'Next post'
 *
 * @return string $links
*/
function get_single_post_navigation( $args = [] ) {

    // Sets the default arguments for default usage
    $defaults = [
        'in_same_term'      => true,
        'parent_term'       => true,
        'post_types'         => '',
        'taxonomy'          => 'category',
        'previous_text'     => __( 'Previous post' ),
        'next_text'         => __( 'Next post' ),
    ];

    // Merges the default arguments with user defined variables
    $args = wp_parse_args( $args, $defaults );

    /**
     * Get the currently displayed single post. For this use
     * get_queried_object() as this is more safe than the global $post
     *
     * The $post global is very easily changed by any poorly written custom query
     * or function, and is there for not reliable
     *
     * @see Post below on WPSE for explanation
     * @link https://wordpress.stackexchange.com/q/167706/31545
    */
    $single_post = get_queried_object();

    /**
     * Use the post type of the current post or post types entered in args
     *
    */
    $post_type   = ( empty( $args['post_types'] ) ) ? $single_post->post_type : $args['post_types'];


    // Set the variable query variables according to condition
    if( !empty( _query_vars_to_query_args() ) ) {

        $query_args = _query_vars_to_query_args();

    }elseif( true === $args['in_same_term'] ) {

        $terms =  wp_get_post_terms( $single_post->ID, $args['taxonomy'] );

        if ( ! empty( $terms ) && ! is_wp_error( $terms ) ){

            foreach ( $terms as $term ) {
                if( $term->parent === 0 ) {
                    $parent[] = $term;
                }else{
                    $child[] = $term;
                }
            }

            $term_id = ( $args['parent_term'] === true && isset( $parent ) ) ? $parent[0]->term_id : $child[0]->term_id;

            $query_args = [
                'tax_query' => [
                    [
                        'taxonomy'          => $args['taxonomy'],
                        'field'             => 'term_id',
                        'terms'             => $term_id,
                        'include_children'  => false,
                    ],
                ],
            ];
        }

    }else{

        $query_args = [];

    }

    // Default arguments to use with all the conditional statements above
    $default_query_args = [
        'post_type'         => $post_type,
        'fields'            => 'ids',
        'posts_per_page'    => -1,
        'suppress_filters'  => true,
    ];

    // Merges the default arguments with the arguments from the conditional statement
    $combined_args = wp_parse_args( $query_args, $default_query_args );

    $q = new WP_Query( $combined_args );

    // Get the current post position. Will be used to determine adjacent posts
    $current_post_position = array_search( $single_post->ID, $q->posts );

    // Get the returned values from '_add_query_vars_to_nav_links()' to build links
    $get_qv = _add_query_vars_to_nav_links();

    // Get the next/older post ID
    if ( array_key_exists( $current_post_position + 1 , $q->posts ) ) {
        $next = $q->posts[$current_post_position + 1];
    }

    // Get post title link to the next post
    if( isset( $next ) ) {

        $next_post      = get_post( $next );
        $next_post_link = ( !empty( $get_qv ) ) ? add_query_arg( $get_qv, get_permalink( $next ) ) : get_permalink( $next );
        $next_title     = '<span class="meta-nav">' . $args['next_text'] . ': </span><a href="'%20.%20$next_post_link%20.%20'">' . $next_post->post_title . '</a></br>';

    }else{

        $next_title     = '';

    }

    // Get the previous/newer post ID
    if ( array_key_exists( $current_post_position - 1 , $q->posts ) ) {
        $previous = $q->posts[$current_post_position - 1];
    }

    // Get post title link to the previous post
    if( isset( $previous ) ) {

        $previous_post      = get_post( $previous );
        $previous_post_link = ( !empty( $get_qv ) ) ? add_query_arg( $get_qv, get_permalink( $previous ) ) : get_permalink( $previous );
        $previous_title     = '<span class="meta-nav">' . $args['previous_text'] . ': </span><a href="'%20.%20$previous_post_link%20.%20'">' . $previous_post->post_title . '</a></br>';

    }else{

        $previous_title     = '';

    }

    // Create the next/previous post links
    $links  = '<nav class="navigation post-navigation" role="navigation">';
    $links .= '<div class="nav-links">';
    $links .= $previous_title;
    $links .= $next_title;
    $links .= '</div><!-- .nav-links -->';
    $links .= '</nav><!-- .navigation -->';

    // Returns the post links with HTML mark-up
    return $links;

}

/**
 * This function is simply just a wrapper for the main navigation
 * function and echo's the returned values from the main navigation
 * function
*/
function single_post_navigation( $args = [] ) {

    echo get_single_post_navigation( $args );

}

在單一模式下使用

如果您不需要在同一術語中瀏覽帖子,請從所有帖子型別發帖,並透過連結自定義下一個和上一個文字,您可以執行以下操作:

$args = [
    'in_same_term'     => false,
    'post_types'       => ['post', 'my_post_type'],
    'previous_text'      => __( 'Vorige Pos' ),
    'next_text'      => __( 'Volgende Pos' ),
];

single_post_navigation( $args );

編輯 1

根據 SO 上的帖子的要求,作為 @todo 的一部分,我現在已經介紹了支援,不僅可以在當前帖子的帖子型別的帖子之間導航,而且還可以使用使用者使用 post_types 引數設定的一組帖子型別功能。請參閱更新的程式碼。

編輯 2

'suppress_filters' => true, 新增到 WP_Query 引數,以使分頁不被 WP_Query 中使用的任何過濾器更改

參考文獻

注:本文內容整合自 Google/Baidu/Bing 輔助翻譯的英文資料結果。如果您對結果不滿意,可以加入我們改善翻譯效果:薇曉朵技術論壇。