0

I've written a shortcode for fetching the most viewed posts in the past week. I use a filter as the following in my shortcode's function:

function weeks_popular(){
    //Filter the date
    function filter_where($where = '') {
        $where .= " AND post_date > '" . date('Y-m-d', strtotime('-7 days')) . "'";
        return $where;
    }
    add_filter('posts_where', 'filter_where');
    $pops = new WP_Query( 
                array( 
                    'posts_per_page' => 4,
                    'meta_key' => 'views',
                    'orderby' => 'meta_value_num',
                    'order' => 'DESC' 
                ) 
            );
    //If there is a post, start the loops
    if ($pops->have_posts()) {
        $pops_content='<div class="shortcode">';
        while ($pops->have_posts()){ 
            $pops->the_post(); 
            $pops_content .= '<a href="'.get_the_permalink().'">'.get_the_title().'</a>';
        }
        $pops_content .= '</div>';
        return $pops_content;
    } 
    //Remove the date filter
    remove_filter('posts_where', 'filter_where'); 
    wp_reset_postdata();        
}
add_shortcode( 'recent-posts', 'weeks_popular' );

I used this in a PHP widget before, and it worked fine. Now, there is a problem with it : It affects every other query on the page, including other shortcodes.

I'm resetting the post data after the query has finished, so i don't know what's wrong, since it used to work (and still does work) in a PHP widget. Just doesn't work in shortcode.

Am i missing something?

  • Can you post the code of your widget with the loop intact? It's near impossible to debug a post loop if you can't see it – Tom J Nowell May 8 '17 at 18:12
  • @TomJNowell Thanks for the comment tom, i added the full code. The other shortcodes are EXACTLY the same, just without the filter. – Jack Johansson May 8 '17 at 20:16
  • @JackJohansson If you spit out the WP_Query Request $pops->request and run that SQL directly ( PHPMyAdmin or otherwise ) does it produce the correct results? – Howdy_McGee May 8 '17 at 20:19
  • @Howdy_McGee Yes, it does. If i simply copy-paste this code to a PHP widget, it works without interfering. But when i use it as a shortcode, it interferes. I have to note, i use other shortcodes which have Query inside them too (all in the same page). – Jack Johansson May 8 '17 at 20:32
2

Note that you're returning from the shortcode's callback with:

return $pops_content;

before removing the filter's callback with:

remove_filter('posts_where', 'filter_where'); 

So it's never called.

That means you're affecting all the later WP_Query instances with your filter.

Note that you can use date_query in WP_Query instead, so you don't need the posts_where filtering.

  • I figured date_query just a minute before i read your answer ;) However your answer is correct. Thank you. – Jack Johansson May 8 '17 at 21:03
2

Ordering is the problem here:

//If there is a post, start the loops
if ($pops->have_posts()) {
    $pops_content='<div class="shortcode">';
    while ($pops->have_posts()){ 
        $pops->the_post(); 
        $pops_content .= '<a href="'.get_the_permalink().'">'.get_the_title().'</a>';
    }
    $pops_content .= '</div>';
    return $pops_content;
} 
//Remove the date filter
remove_filter('posts_where', 'filter_where'); 
wp_reset_postdata();   

Your code will never call wp_reset_postdata or remove the filter as it returns early after the while loop.

You should instead return at the end of the function, after the filter has been removed and the postdata reset.

A final note

Always calling wp_reset_postdata isn't wise. It's purpose is to cleanup, but if you never set any postdata, then what are you resetting? This can cause problems in nested loops, so always put the call inside the if statement just after the while loop.

  • Thanks, this actually worked. I always forget this fact about return, that it really DOES return to the the caller, dropping the rest of the code. – Jack Johansson May 8 '17 at 21:04
1

I resolved the issue by forgetting filters, and using a date query:

$pops = new WP_Query( 
        array( 
            'posts_per_page' => 4,
            'meta_key' => 'views',
            'orderby' => 'meta_value_num',
            'order' => 'DESC',
            'date_query' => array(
                array(
                    'year'  => date('Y'),
                    'week' => (date('W')-1),
                ),
            )
        ) 
    );

Both of the provided answers by @birgire and @Tom are correct, and worked. However i marked the first answer as accepted.

  • 1
    ps: before/after also understand strtotime strings, like 'before' => '7 days ago'. I remember playing with it here. – birgire May 8 '17 at 21:13
  • @birgire thanks for the link. will it cause any issue if i just use my posted answer? – Jack Johansson May 8 '17 at 21:17
  • What will happen to your current year/week date query, 1st of January 2018? Then you're searching for week 0! – birgire May 8 '17 at 21:20
  • @birgire haha yes! that would return santa as a result! i forgot about that. will follow the link, tnx. i actually noticed there is no need to use -1 if i wanted to get the results for this week! – Jack Johansson May 8 '17 at 21:21

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.