Получить структуру постоянных ссылок по умолчанию из красивых URL

15

Если щелкнуть одну публикацию на странице категории или, если на то пошло, любую страницу, вы можете получить URL этого реферера и проанализировать его, чтобы получить строку запроса. Но это работает только со структурой постоянной ссылки по умолчанию

Пример, когда рефералом является страница категории:

A var_dump( parse_url( wp_get_referer() ) );дает следующий вывод со структурой постоянной ссылки по умолчанию

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%/структуры постоянной ссылки.

Питер Гусен
источник
Это звучит как библиотека Url To Query кем-то, кого вы могли бы сейчас. ;)
'18
Зачем ты это делаешь? (Проблема XY)
Том Дж. Новелл
Спасибо @Rarst Должен был знать, что у кого-то будет что-то вроде этого :-). Это будет соответствовать всем требованиям
Питер Гусен
Я все еще хотел бы видеть ответ от GM, объясняющего, как это работает.
'11
@ TomJNowell В основном мне нужно знать, была ли ссылка отправлена ​​со страницы терминов таксономии, если да, мне нужно знать, какой термин. Мне нужно рассчитать, какой пост будет служить следующим / предыдущим постом. Это будет необходимо только в том случае, если сообщения имеют несколько терминов. Отдельные термины по всем направлениям просты
Питер Гусен

Ответы:

10

Прежде всего, я должен сказать, что wp_get_referer()это не на 100% надежно, потому что полагается на $_SERVER['HTTP_REFERER']это, не на 100% надежно, из документации php.net :

Адрес страницы (если есть), которая направила пользовательский агент на текущую страницу. Это устанавливается агентом пользователя. Не все пользовательские агенты будут устанавливать это, а некоторые предоставляют возможность изменять HTTP_REFERER как функцию. Короче говоря, этому нельзя доверять.

Альтернативное решение

Если вы можете добавить к URL-адресу публикации аргумент запроса, в котором указано, откуда поступило сообщение, оно будет более надежным, и вам не нужно анализировать 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']и без каких-либо других усилий.

Ответь на свой вопрос

Получение информации о запросе, начиная с URL, - это то, что WordPress делает внутри WP::parse_request()метода.

Этот метод предназначен для использования только один раз и только для «основного» URL (URL, который просматривает пользователь), а не для произвольных URL.

Несколько месяцев назад я написал плагин Url To Query с целью сделать то же самое для произвольных URL.

Что я сделал, так это взял WP::parse_request()и реорганизовал его в более разумный код ООП и заставил работать с произвольными URL-адресами (например, URL-адрес для обработки принимается в качестве аргументов, а не из$_SERVER вар).

Используя мой плагин вы можете

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

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

Таким образом, вы получаете аргументы запроса (то, что вы можете прямо передать new WP_Query), начиная с URL, это именно то, чтоWP::parse_request() делает.

В вашем случае вы, вероятно, могли бы проверить массив args без необходимости фактически выполнять запрос.

Это, безусловно, может сработать, однако я думаю, что дополнительные усилия, необходимые для анализа URL-адреса и ненадежности, $_SERVER['HTTP_REFERER']делают первое решение лучше для ваших целей.

Gmazzap
источник
Если я хочу получить Id или slug сообщения от реферера .. Как я могу получить это .. поскольку объект запроса не содержит эту информацию ...
Парт Кумар
5

Первоначально целью этого вопроса было выяснить, откуда был отослан один пост, и затем, в соответствии с этим, поделить следующие и предыдущие посты в соответствии с рефералом.

То, что я хотел сделать, например:

Нажатие на публикацию на странице категории, таксономии, тега, поиска или авторского архива. Эти архивы служат справочниками. Теперь обычно можно использовать, как в моем вопросе, wp_get_referer()чтобы получить этот реферер и использовать его в дальнейших запросах. Как описано @GM в его принятом ответе выше , этот метод ненадежен , поэтому я пошел и использовал его Альтернативное решение .

Другая проблема заключалась в необходимости использования какого-либо файла cookie или сеанса для хранения этого реферера, чтобы вы все равно собирали записи из исходного реферера, когда уходите от оригинального отдельного поста, который был выбран из определенного архива. Поскольку cookie-файлы также контролируются конечным пользователем и, следовательно, ненадежны, а также тот факт, что WordPress не использует сеансы по умолчанию, я реорганизовал ссылки на следующую и предыдущую публикацию, используя @GM Alternative Solution чтобы иметь надежный способ проверки и хранения моего оригинала. реферер.

Это то, что я придумал, и я надеюсь, что кто-то найдет это полезным в ближайшем будущем. Пожалуйста, используйте и злоупотребляйте кодом, чтобы соответствовать вашим потребностям, только один запрос: оставьте ссылку на этот вопрос. :-)

ЗАМЕЧАНИЯ ПО КОДУ

  • Этот код довольно длинный и интенсивный, поэтому я не буду вдаваться в подробности. Код был хорошо прокомментирован

  • Этот код имеет опцию для постинга между постами в пределах одного и того же термина, как по умолчанию next_post_link()и previous_post_link()функций в WordPress. Так же, как нативные функции, вы должны установить таксономию. Значение по умолчанию для 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 /wordpress//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="' . $next_post_link . '">' . $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="' . $previous_post_link . '">' . $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

Питер Гусен
источник