問題描述

當從類別頁面或任何頁面點擊單個帖子時,您可以獲取該引用來源的 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 輔助翻譯的英文資料結果。如果您對結果不滿意,可以加入我們改善翻譯效果:薇曉朵技術論壇。