Preserving postdata with embedded wp_query queries?

I’m designing a website in WordPress. This site is one of those parallax sites where all the pages are printed on the homepage and the menu scrolls to the anchors.

That being said I am using a wp_query to pull out all the pages that are in the main menu. Furthermore I have a shortcode that I use in the content that also requires the use of wp_query.

Read More

The problem I have is that the shortcode (the embedded wp_query) is screwing up the postdata. I know when using wp_query you’d usually want to use wp_reset_postdata but in this particular situation it doesn’t work because this function call will restore the postdata of the homepage and not of the currently running wp_query (sorry if I’m being unclear).

Is there a way to take a snapshot of the postdata to then restore after my shortcode? I’m looking for something along the lines of:

function my_shortcode() {
    save_postdata(); //saves the current postdata
    $query = new WP_Query();
    while( $query->have_posts() )   {
        $query->the_post();
        echo get_the_title();
    }
    my_wp_reset_postdata(); //restores the postdata to where it was before the loop
}

Related posts

Leave a Reply

3 comments

  1. By looking in the source for wp_reset_query(), you will see that what it does is that it simply restores the $wp_query global variable from another global variable($wp_the_query – this is set-up together with the initial set-up for $wp_query, so it holds the original query).

    What you can do is you can simply assign $wp_query to a different global variable and then later restore it. Here’s an example:

    function _save_query( $var = '_wp_query' ) {
        $GLOBALS[ $var ] = $GLOBALS['wp_query'];
    }
    
    function _wp_reset_query( $var = '_wp_query' ) {
        $GLOBALS['wp_query'] = $GLOBALS[ $var ];
        wp_reset_postdata();
    }
    

    So simply call _save_query() before overwriting the query(you can pass a custom variable name – this way you can store multiple WP_Query objects 🙂 ).

    Once you want to restore the query data, call _wp_reset_query() – again you can pass a string as a variable name in order to restore this exact query object.

  2. This is how I managed to get it working, credit goes to Nikola’s question since I worked off of his idea.

    function _save_query( $var = '_wp_query' ) {
        global $post;
        $GLOBALS[ $var ] = $post;
    }
    
    function _wp_reset_query( $var = '_wp_query' ) {
        global $post;
        $post = $GLOBALS[ $var ];
        setup_postdata( $post );
    }
    

    I looked at the documentation of how the loop works found here. I decided to use the same kind of setup as in Nikola’s answer since it met my criteria but I used the implementation of the_post to restore the postdata. This is probably not very efficient since it’s using the setup_postdata function (which I assume is overkill) but it has definitely solved my problem.

    So now when I embed a wp_query I can just do the following:

    _save_query();
    $products = new WP_Query( $args );
    if( $products->have_posts() ) {
        $ob .= '<ul class="group-posts">';
        while ( $products->have_posts() ) {
            $products->the_post();
            $ob .= '<li>'.get_the_title().'</li>';
        }
        _wp_reset_query();
        $ob .= '</ul>';
    }
    

    Side question/note: What’s the etiquette for marking an answer as the correct answer? I’d feel bad accepting my answer as the correct one when Nikola helped me reach it?

  3. my_wp_reset_postdatadoes not exist. You have to use wp_reset_postdata(). But in a situation where you have to chain multiple wp_queries and come back to the older ones, you can store your first query in a variable, set the new WP_query, then reset it and come back to the old one.

    $wp_query stores the current loop. So you can go something like :

        $temp = $wp_query;
        $wp_query= null;
        $wp_query = new WP_Query($args);
        ...
    
        // then later 
        $wp_query = $temp;
        // And back on tracks !