1

UPDATED QUESTION to be CLEARER

Here's my situation:

  • Wordpress top navigation has several menu items that jump to an array of posts within a specific category. For example, if you click "necklaces" you will find yourself in the necklaces section. You can click "next" or "previous" buttons and advance to the next necklace post.

  • At the top of the page, there is an H2 tag which displays the title by getting its category name:

    <?php
        $category = get_the_category();
        echo $category[0]->cat_name;
    ?>
    
  • The problem is that all these posts are listed under MULTIPLE categories. The above code only gets the first category. If one post is listed under the categories of "necklaces" as well as "pendants" then it's going to display the wrong title in one of these categories.

So, I need to have the title display the correct category according to the actual category the user is in at the moment. Currently, when you click "next" or "previous", the title will randomly change depending on which category is listed as first for that particular post.

There are dozens of categories. They all share the single.php template. I am trying to change the title on the single.php template page NOT category.php

  • Your questions makes no sense at all. Please elaborate more (through an edit please) and be more specific with the problem you are facing – Pieter Goosen Jun 18 '15 at 18:57
  • Alright, I completely rewrote the question very specifically. Thank you for your feedback. It helped me hopefully clarify for anyone who comes along. – Nate Jun 18 '15 at 19:57
  • @Nate It sounds like some products are in multiple categories and you need to pull the current category that the user is viewing, is that correct? – Howdy_McGee Jun 18 '15 at 20:01
  • @Howdy_McGee yes, I think that is exactly right. The problem is that most of these are in a parent category and then they are also in a bunch of other categories like necklaces, pendants, new arrivals, etc. in no particular order. If I select "pendants" from the top navigation, it gives me an array of posts for "pendants" so I'd like to simply make sure that the title says "pendants" on the top of each page when I click "next" or "previous." Currently, it displays differently depending on whatever categories each post is in. There are dozens of categories. All posts use single.php. – Nate Jun 18 '15 at 20:24
  • @Nate if you click the "edited" link above my name you can see the revision list. I kept the same content, removed the bullets as they weren't needed, and removed the last repetitive paragraph for clarification. If you feel after looking at edit revisions that I didn't capture the question like you though, you can roll the edit back. Editing Help – Howdy_McGee Jun 18 '15 at 21:05
2

In order for this to work, you will need to know from where the single post was refered from and if the referrer is a valid one. Secondly, we need to adjust pagination between single posts in order for us to pass our referrer between single posts.

The problem with referrers is, they are set and controlled by user side. Referrers can be disabled or blocked (like users accessing a site behind a proxy). Just for interest sake, here is very interesting answer to the question In what cases will http referrer be empty taken from this post on SO

It will/may be empty when the enduser

  • entered the site URL in browser address bar itself.
  • visited the site by a browser-maintained bookmark.
  • visited the site as first page in the window/tab.
  • switched from a https URL to a http URL.
  • switched from a https URL to a different https URL.
  • has security software installed (antivirus/firewall/etc) which strips the referrer from all requests.
  • is behind a proxy which strips the referrer from all requests.
  • visited the site programmatically (like, curl) without setting the referrer header (searchbots!).

You would want to read the other answers there as well for extra insight

I sat with similar issue and was looking for a more reliable way to set and pass referrers, and this led to this question and a wonderful answer by @gmazzap

So your solution would be based on something similar. We will be adding a special parameter to our URL which will serve as referrer (I have made the assumption that your posts are from the build in post type post and taxonomy category)

  • Identify our category pages and add a referrer to the post links via the post_link filter. This referrer will hold the category id. You can use the category name, but this can become quite messy in your URL. The choice is all up you.

    add_filter('post_link', function( $permalink ) // Change to post_type_link for custom post type posts
    {
        if (      is_category() // Change to is_tax() for custom taxonomy pages
             && ( $cat = get_queried_object() ) 
        ) {
            $permalink = esc_url( add_query_arg( array( 'ref' => $cat->term_id ), $permalink ) );
        }
        return $permalink;
    });
    

    This will give you a URL like the following on your single post page if you click on a post link on the category page

    http://example.com/wordpress/post-name?ref=1
    
  • We must have a way to read and use that info from the URL in order to make it useful. For this, we must add a the query arg ref so Wordpress can read it

    add_filter( 'query_vars', function ( $vars ) 
    {
        $vars[] = 'ref';
        return $vars;
    });
    
  • All you need to do now, is to check if the single post was a referred post, get the value from the URL and use that info to display the correct category name (NOTE: This piece of code goes into your single.php)

    $referrer = filter_input( INPUT_GET, 'ref', FILTER_VALIDATE_INT ); // This validate and checks if our referrer "ref" is set
    if ( $referrer ) {
        $category = get_category( $referrer );
        echo '<h2>' . $category->name . '</h2>';
    }
    

The above is just a rough draft of what you can do to accomplish this, it just gives you a foundation to work from. You can tweak and modify it as needed.

On your single pages, you will also need to get your referrer and pass that to your next and previous post links in order to maintain this feature between posts. I have written an answer for pagination based on this, it is quite bulky though and can use some clean up. There are some parts that I have handled here now, and there are also parts that you will most probably never need which you can just remove. You can work through it and modify and adjust it as needed.

I am busy rewritting the complete pagination function ( using better and cleaner php functionality ) with some extra features, the classes are done, it is just the final pieces putting everything into functional functions that is giving me a nightmare, and to be honest, I haven't got back to working on the project for quite some time now as I really don't have much time at night. When it will be completed is still a mystery. But the idea and code from what I have given in that specific answer should do the trick for you. You can check out my answer and referrer pagination function here

EDIT

I have quickly written a small plugin which covers the referrer and new pagination links to carry the referrer across multiple single posts. This pagination links will also page between single posts from the referrer

What this means is, if a single post was clicked from category B, and this post has three categories, category A, category B and category C, the next and previous adjacent post will be from category B. If you go to either post, the adjacent post from that clicked single post will also be from category B etc

I have commented the code so you should be able to follow it better and also adjust and modify it as needed. Just copy and paste this code in a plugin file and activate it. Alternatively, copy the code (without the plugin header of course) and paste it as is into your functions.php (NOTE: This only works for the build in taxonomy category and the build in post type post. You should modify it accordingly to your needs according to post type and custom taxonomy)

<?php
/*
 * Plugin Name: Category referred posts
 * URI: https://wordpress.stackexchange.com/a/192018/31545
 * Description: Add referrer links to single posts and pagination if single posts was referred from a category page
 * Version: 1.0
 * Author: Pieter Goosen
 */

/*
 * Add our custom query vars so Wordpress can read it
 */
add_filter( 'query_vars', function ( $vars ) 
{
    $vars[] = 'ref';
    return $vars;
});

/* 
 * Add our referrer to single post links if we are on a category page
 */
add_filter('post_link', function( $permalink ) // Change to post_type_link for custom post type posts
{
    if (      is_category() // Change to is_tax() for custom taxonomy pages
         && ( $cat = get_queried_object() ) 
    ) {
        $permalink = esc_url( add_query_arg( ['ref' => $cat->term_id], $permalink ) );
    }
    return $permalink;
});

/*
 * Create our custom adjacent post link 
 */
function get_referred_adjacent_post( $args = [] ) 
{
    //First check if we are on a single post, else return false
    if ( !is_single() )
        return false;

    //Defaults arguments set for the function. 
    $defaults = [
        'previous'       => true,
        'anchor_text'    => '%anchor',
        'post_link_text' => '%text',
        'span_text_prev' => __( 'Older post: ' ),
        'span_text_next' => __( 'Newer post: ' ),
    ];  

    $combined_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
    */ 
    $current_post       = get_queried_object();
    $current_post_date  = $current_post->post_date;
    $current_post_type  = $current_post->post_type;

    //Set the important parameters to either get the next post or previous post
    $previous = $combined_args['previous'];
    $order    = ( $previous ) ? 'DESC' : 'ASC';
    $op       = ( $previous ) ? 'before' : 'after';

    // Check if we have a referrer, if so, we need to set this to get the next post in this specific referrer category
    $cat_id = filter_input( INPUT_GET, 'ref', FILTER_VALIDATE_INT );

    if ( $cat_id )
        $custom_args = ['cat' => $cat_id];

    /**
     * Set the default arguments to merge with the referrer arguments
     *
     * Uses date_query (introduced Wordpress 3.7) to calculate the appropriate adjacent post
     * @see http://codex.wordpress.org/Class_Reference/WP_Query#Date_Parameters
    */ 
    $query_args = [
        'post_type'         => $current_post_type,
        'posts_per_page'    => 1,
        'order'             => $order,
        'no_found_rows'     => true,
        'suppress_filters'  => true,
        'date_query'        => [
            [
                $op         => $current_post_date,
                'inclusive' => false
            ]
        ]
    ];

    $query_args = ( isset( $custom_args ) ) ? wp_parse_args( $custom_args, $query_args ) : $query_args;

    $q = new WP_Query( $query_args );

    //If there are no post found, bail early
    if( !$q->have_posts() === 0 )
        return false;

    //If there are posts, continue
    $adjacent_post = $q->posts[0];

    //Build the permalinks for the adjacent post
    $permalink = get_permalink( $adjacent_post->ID );

    // Return the correct permalink, we should add our referrer to the link now if this post was referred
    $link = ( $cat_id ) ? add_query_arg( ['ref' => $cat_id], $permalink ) : $permalink;

    // Set up out link text to display
    $span_text = ( $combined_args['previous'] ) ? $combined_args['span_text_prev'] : $combined_args['span_text_next'];
    $span = '<span class="meta-nav">' . $span_text . '</span>';

    // Create our anchor and post title text. By default. The post title is used
    $anchor_text = ( $combined_args['anchor_text'] == '%anchor' )  ? $adjacent_post->post_title : $combined_args['anchor_text'];
    $post_title  = ( $combined_args['post_link_text'] == '%text' ) ? $adjacent_post->post_title : $combined_args['post_link_text'];

    //Create the link with title name and anchor text
    $adjacent_post_link = $span . '<a href="' . $link . '" title="' . $anchor_text . '">' . $post_title . '</a>';

    return $adjacent_post_link;
}

// Create the next post link - Return the post link
function get_next_adjacent_post_link( $anchor_text = '%anchor', $post_link_text = '%text', $span_text_next = 'Newer post: ' )
{
    $args = [
        'previous'       => false,
        'anchor_text'    => $anchor_text,
        'post_link_text' => $post_link_text,
        'span_text_next' => $span_text_next,
    ];
    return get_referred_adjacent_post( $args );
}

// Create the previous post link - Return the post link
function get_previos_adjacent_post_link( $anchor_text = '%anchor', $post_link_text = '%text', $span_text_prev = 'Older post: ' )
{
    $args = [
        'previous'       => true,
        'anchor_text'    => $anchor_text,
        'post_link_text' => $post_link_text,
        'span_text_prev' => $span_text_prev,
    ];
    return get_referred_adjacent_post( $args );
}

// Create the next post link - Echo post link
function next_adjacent_post_link( $anchor_text = '%anchor', $post_link_text = '%text', $span_text_next = 'Newer post: ' )
{
    echo get_next_adjacent_post_link( $anchor_text, $post_link_text, $span_text_next );
}

// Create the previous post link - Echo post link
function previos_adjacent_post_link( $anchor_text = '%anchor', $post_link_text = '%text', $span_text_prev = 'Older post: ' )
{
    echo get_previos_adjacent_post_link( $anchor_text, $post_link_text, $span_text_prev );
}

You should now add the following in your single.php:

  • To display your custom text

    $referrer = filter_input( INPUT_GET, 'ref', FILTER_VALIDATE_INT ); // This validate and checks if our referrer "ref" is set
    if ( $referrer ) {
        $category = get_category( $referrer );
        echo '<h2>' . $category->name . '</h2>';
    }
    
  • To display your post links

    if ( function_exists( 'next_adjacent_post_link' ) )
        next_adjacent_post_link();
    if ( function_exists( 'previos_adjacent_post_link' ) )
        previos_adjacent_post_link();
    

    Just add your relevant mark up to your links. Also check the accepted arguments to customize the text the links display.

This should lay the foundation to set you on your way. You should be able to customize this to suit your exact needs

  • Thank you very much for the in-depth response. I haven't had a chance to look at this problem any more yet and am about to head into a meeting that will last at least 2 hours. I also found this late last night: wordpress.org/support/topic/… ...which I have yet to look into. Thank you very much for returning and helping me out. – Nate Jun 19 '15 at 14:39
  • Jip, we need to clean up our comments again so we keep our side clean. I do agree that someone needs to look at your theme etc. There are some serious bugs in your theme or a plugin which is stuffing you around. Unfortunately you will need some kind of knowledge and must be confident enough to get your hands dirty and to debug this issue. I'm not being rude, I really want to help you to solve this, but this is as far as I can help. It is really not possible to say what your issue is if I know my code works and I just cannot reproduce your issue – Pieter Goosen Jun 25 '15 at 17:34
  • I don't know about that. It's probably just like you said: either a query_post or a plugin that conflicts. Not sure why you would believe something is wrong with anything that conflicts with it. Also, I don't think you're being rude. It's obvious you're trying to help me by your persistence and I appreciate that, but I pretty much gave up a while ago and only kept doing stuff because you seemed to want me to try. I didn't build the theme, so I'm not defending it. But, we need the plugins & the set-up is complex, so I'm not going to "debug" those issues. Don't even think they're bugs, frankly. – Nate Jun 25 '15 at 23:19
0

What's the simplest way to spit out a single category as a title in an H2 tag for a post with multiple categories?

$c = get_the_category();
if (!empty($c[0])) {
  echo $c[0]->name;
}

I have no idea how or why sessions would come into that.

  • Thanks, but the problem I'm having with this method is the multiple categories. Some of these posts have 5 categories and this option doesn't intelligently choose a category, it just selects the first one. That is why I thought of sessions. I thought maybe I could grab the category from when I initially click the top menu and it brings me into that array and then store it as a variable that will remain constant until it is changed, so when you click "next" and "previous" the title won't randomly change if the next post has a different category come first. – Nate Jun 18 '15 at 18:54
0

I have been looking for like two days before I found this, why this is not standard behavior in WP is beyond me. The only thing I did find were plugins that were no longer working and code that did not take into account that there are hierarchical categories and that there could be multiple tags and categories attached to a post.

FYI: I expanded the code van Pieter Goosen to also include the tags. I also corrected something the showing of next and previous links at the end of the line to posts that were not there. And I decided to separate the normal next-prev-post stuff from the next-pref stuff from this code in single.php. The thing that I did not get to work was using menu_order as sorting - since I have a custom post order - ah well.

This is now in my functions.php:

/*
 * Add our custom query vars so Wordpress can read it
 */
add_filter( 'query_vars', function ( $vars )
{
    array_push($vars, "ref", "thetype");
    return $vars;
});

/*
 * Add our referrer to single post links if we are on a category page
 */
add_filter('post_link', function( $permalink ) // Change to post_type_link for custom post type posts
{
    if (      is_category() // Change to is_tax() for custom taxonomy pages
         && ( $cat = get_queried_object() )
    ) {
        $permalink = esc_url( add_query_arg( ['ref' => $cat->term_id], $permalink ) );
        $permalink = esc_url( add_query_arg( ['thetype' => 'category'], $permalink ) );
    }

    if (      is_tag() // Change to is_tax() for custom taxonomy pages
         && ( $cat = get_queried_object() )
    ) {
        $permalink = esc_url( add_query_arg( ['ref' => $cat->term_id], $permalink ) );
        $permalink = esc_url( add_query_arg( ['thetype' => 'tag'], $permalink ) );
    }

    return $permalink;

});

/*
 * Create our custom adjacent post link
 */
function get_referred_adjacent_post( $args = [] )
{
    //First check if we are on a single post, else return false
    if ( !is_single() )
        return false;

    //Defaults arguments set for the function.
    $defaults = [
        'previous'       => true,
        'anchor_text'    => '%anchor',
        'post_link_text' => '%text',
        'span_text_prev' => __( 'Older post: ' ),
        'span_text_next' => __( 'Newer post: ' ),
    ];

    $combined_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
    */
    $current_post       = get_queried_object();
    $current_post_date  = $current_post->post_date;
    $current_post_type  = $current_post->post_type;
    $current_post_ID    = $current_post->ID;


    //Set the important parameters to either get the next post or previous post
    $previous = $combined_args['previous'];
    $order    = ( $previous ) ? 'DESC' : 'ASC';
    $op       = ( $previous ) ? 'before' : 'after';
    $css_class = $combined_args['css_class'];

    // Check if we have a referrer, if so, we need to set this to get the next post in this specific referrer category
    $mytype = filter_input( INPUT_GET, 'thetype');
    if ($mytype == 'category')
        {
            $cat_id = filter_input( INPUT_GET, 'ref', FILTER_VALIDATE_INT );
             if ( $cat_id )
            $custom_args = ['cat' => $cat_id];

        }
    else
        {
        $cat_id = filter_input( INPUT_GET, 'ref', FILTER_VALIDATE_INT );
             if ( $cat_id )
            $custom_args = ['tag_id' => $cat_id];

        }


    /**
     * Set the default arguments to merge with the referrer arguments
     *
     * Uses date_query (introduced Wordpress 3.7) to calculate the appropriate adjacent post
     * @see http://codex.wordpress.org/Class_Reference/WP_Query#Date_Parameters
    */
    $query_args = [
        'post_type'         => $current_post_type,
        'posts_per_page'    => 1,
        'order'             => $order,
        'no_found_rows'     => true,
        'suppress_filters'  => true,
        'date_query'        => [
            [
                $op         => $current_post_date,
                'inclusive' => false
            ]
        ]
    ];

    $query_args = ( isset( $custom_args ) ) ? wp_parse_args( $custom_args, $query_args ) : $query_args;

    $q = new WP_Query( $query_args );

    //If there are no post found, bail early
    if( !$q->have_posts() === 0 )
        return false;

    //If there are posts, continue
    $adjacent_post = $q->posts[0];

    if( empty($adjacent_post->ID) ) return false;

    //Build the permalinks for the adjacent post
    $permalink = get_permalink( $adjacent_post->ID );

    // Return the correct permalink, we should add our referrer to the link now if this post was referred
    $link = ( $cat_id ) ? add_query_arg( ['ref' => $cat_id, 'thetype' => $mytype], $permalink ) : $permalink;


    // Set up out link text to display
$span_text = ( $combined_args['previous'] ) ? $combined_args['span_text_prev'] : $combined_args['span_text_next'];
$span = '<span class="meta-nav">' . $span_text . '</span>';

// Create our anchor and post title text. By default. The post title is used
$anchor_text = ( $combined_args['anchor_text'] == '%anchor' )  ? $adjacent_post->post_title : $combined_args['anchor_text'];
$post_title  = ( $combined_args['post_link_text'] == '%text' ) ? $adjacent_post->post_title : $combined_args['post_link_text'];

//Create the link with title name and anchor text
$adjacent_post_link = $span . '<a href="' . $link . '" class="' . $css_class . '" title="' . $anchor_text . '">' . $post_title . '</a>';
    return $adjacent_post_link;
}

// Create the next post link - Return the post link
function get_next_adjacent_post_link( $anchor_text = '%anchor', $post_link_text = '%text', $span_text_next = 'Newer post: ' )
{
    $args = [
        'previous'       => false,
        'anchor_text'    => $anchor_text,
        'post_link_text' => $post_link_text,
        'span_text_next' => $span_text_next,
        'css_class'      => "post-nav-next",
    ];
    return get_referred_adjacent_post( $args );
}

// Create the previous post link - Return the post link
function get_previos_adjacent_post_link( $anchor_text = '%anchor', $post_link_text = '%text', $span_text_prev = 'Older post: ' )
{
    $args = [
        'previous'       => true,
        'anchor_text'    => $anchor_text,
        'post_link_text' => $post_link_text,
        'span_text_prev' => $span_text_prev,
        'css_class'      => "post-nav-prev",
    ];
    return get_referred_adjacent_post( $args );
}

// Create the next post link - Echo post link
function next_adjacent_post_link( $anchor_text = '%anchor', $post_link_text = '%text', $span_text_next = 'Next post ' )
{
    echo get_next_adjacent_post_link( $anchor_text, $post_link_text, $span_text_next );
}

// Create the previous post link - Echo post link
function previos_adjacent_post_link( $anchor_text = '%anchor', $post_link_text = '%text', $span_text_prev = 'Previous post ' )
{
    echo get_previos_adjacent_post_link( $anchor_text, $post_link_text, $span_text_prev );
}

From the single.php:

<?php $referrer = filter_input( INPUT_GET, 'ref', FILTER_VALIDATE_INT );?>
<!-- // checks if our referrer "ref" is set -->
<?php if ( $referrer ): ?>

        if ( function_exists( 'next_adjacent_post_link' ) )
        next_adjacent_post_link();
        if ( function_exists( 'previos_adjacent_post_link' ) )
        previos_adjacent_post_link();

<?php else: ?> <!--else referer -->

    <!--normal prev next links here -->

<?php endif; ?> <!-- end if refferer -->
  • I just noticed the 'Post Types Order' plugin does not go well with this code - it makes the next button always jump to the last one. The previous button works... – YuriGoul Aug 18 '17 at 22:12
  • Yuri, I just got a notification in my email that you replied. Only took a couple months to get that notification. :) Yeah, it's pretty strange this isn't standard in the WP codebase by now (and how have they never thought of this on their own?). I gave up on it 2 years ago. Sounds like you ran into the same sort of issues I did. If you ever figure out a solution, I'd love to hear it. I just told the client at the time "can't be done. sorry." – Nate Oct 13 '17 at 17:05
  • @Nate I have seen queries based on the menu_order var in WP, but could not get them to work for the next and prev stuff. That said, I kinda understand queries in mysql, but am far from a master. If I decided to dive into that again and it yields results, I will let you know – YuriGoul Oct 15 '17 at 19:17

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.