1

I'm trying to run a query twice, once in a template part (page.php) and once in theme's functions.php. I'm doing this because i need to output some styles to theme's header, since i'm not allowed to use hardcoded inline styles.

This is my main query:

if (have_posts()) {
    while (have_posts()) { 
       the_post();
        // Do some stuff, like the_permalink();
    }
}

Now this is the way i'm outputting my styles to the header:

function header_styles(){
    if (have_posts()) {
        global $post;?>
        <style><?php
            while (have_posts()){
                the_post();
                echo " .background-".$post->ID." { background-image: url('".get_the_post_thumbnail_url( $post->ID,'thumbnail' )."');}";
                print_r($post);
            }
            wp_reset_postdata();?>
        </style><?php
    }
}
add_action('wp_head','header_styles');

This works fine on homepage and archive pages. But now i'm stuck in accessing a custom query. If i assign the query to a variable in the first code and use it in a custom template file (such as My Page which is created using custom-page.php), i can no longer access the query. For example, using this code in custom-page.php:

$custom_query = new WP_Query($args);
if ($custom_query->have_posts()) {
    while ($custom_query->have_posts()) { 
       $custom_query->the_post();
        // Do some stuff, like the_permalink();
    }
}

Now i can output the custom query, but can't access it in functions.php.

Is it possible to workaround this?

5
  • What does access and use in another template file mean? The loop just iterates over a query result, it doesn't contain or run any queries on its own.
    – Milo
    Commented Apr 27, 2017 at 12:50
  • To help you please share whole code from both template files so that we can better understand it. Commented Apr 27, 2017 at 12:56
  • @Milo I'm using the above code in 1 template file (for example index.php) to print half of it's data, and then using it again in another template (such as header.php) to output rest of the data. These 2 data combined (header + content) render the post.
    – Johansson
    Commented Apr 27, 2017 at 13:58
  • 1
    wp_head runs before your query runs in the template, you have to do it in reverse if you're generating a new query. As for accessing vars across files, have a read about php variable scope.
    – Milo
    Commented Apr 28, 2017 at 19:58
  • @Milo Unfortunately ThemeForest strongly advises against using global variables in theme. The cache system offered by Howdy actually solved my issue, and i had to reverse it as you mentioned. Thank you both.
    – Johansson
    Commented Apr 29, 2017 at 2:37

2 Answers 2

2

You could cache the results for that page load. It should hit it in header.php, cache the object, and in index.php you can check availability.

Header

$custom_query = new WP_Query( $args );

if( $custom_query->have_posts() ) {

    // Cache Query before loop
    wp_cache_add( 'custom_query', $custom_query );

    while( $custom_query->have_posts() ) { 
       $custom_query->the_post();
        // Do some stuff, like the_permalink();
    }

    wp_reset_postdata();
}

Other Files

$custom_query = wp_cache_get( 'custom_query', $custom_query );

if( ! empty( $custom_query ) && $custom_query->have_posts() ) {

    while( $custom_query->have_posts() ) { 
       $custom_query->the_post();
        // Do some stuff, like the_permalink();
    }

    wp_reset_postdata();
}
5
  • WordPress had a cache system that i didn't know of? That's great! Thanks.
    – Johansson
    Commented Apr 29, 2017 at 2:36
  • This is at least the long way to solve this. Put the query in a global (obviously actually hide it as a static in a function etc) and just rewind it when you need to loop again Commented Apr 29, 2017 at 4:40
  • 1
    @MarkKaplun Looking at wp_cache_set it temporarily writes it to global. IMO by writing your own function to handle the global would be duplicating work that's already in place.
    – Howdy_McGee
    Commented Apr 29, 2017 at 6:00
  • IMO your way is less readable, it requires people to know yet another rarely used API, which basically is used for very basic php operation, and I am truly afraid of the unexpected side effects that come from using such "storage" techniques on complex objects because it is not always clear at what state they are stored. For example in this case if you "store" after the loop had run the first time you will still need to rewind the loop after "fetching" the object again. Commented Apr 29, 2017 at 10:25
  • .... and your code might actually fail in a bad way if some object caching plugin decides to store the value in memecache/apcu/etc and then at the second call there might be a value retrieved even if the first one found no posts at all. Commented Apr 29, 2017 at 10:29
0

I explained my reservations with the answer by @howdy_mcgee in the comments there, so here is what I think is the proper way to do it.

function wpse_265104_get_cached_query($args) {
  static $query;

  $hash = md5(serialize($args))

  if (!isset($query)) {
     $query = array();
  }
  if (!isset($query[$hash])) {
    $query[$hash] = new wp_Query($args);
  }

  $query[$hash]->rewind();
  return $query[$hash];
}

Now all you need to do is call wpse_265104_get_cached_query with the exact same parameters you would have called wp_query and you do not need to worry about execution paths and which code was executed first, and you get the loop in an "fresh" state. As a bonus this function can cache more than one specific query.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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