2

I've been reading this by @nacin and getting to grips with wp_query over query_posts that I used to use.

What I want is:

  1. to put this in a template file

  2. to query all posts of this category, in this case '3'

  3. to display, if available, the first result on the page the latest sticky post

  4. after the first sticky, if one is set, display the rest of the posts excluding that sticky if it was set

Problems I have seen are: - if I do posts_per_page = 1 on the sticky loop, I can not do posts_per_page = -1 on the rest of the posts loop. To workaround this I've just set the number to 999.

I should say now that the code I have works. However this is for a very high traffic site, and I want to make sure this is the best way of doing it, and I'm not sure I'm using wp_query right to do this since the original query's are essentially the same as one another just with and without sticky posts.

global $wp_query;
$wp_query = new WP_Query(array(
    'post_type' => 'post',
    'posts_per_page' => 1,
    'category__in' => 3,
    'post__in'  => get_option( 'sticky_posts' )
));
while ($wp_query->have_posts()) : $wp_query->the_post();
    $exclude_featured = $post->ID;
    echo the_title();
    echo '<br />';
endwhile; 

echo '<br />';
echo '<br />';

global $wp_query;
$args = array_merge(
    $wp_query->query_vars,
    array(
        'post__in' => null,
        'posts_per_page' => 999,
        'ignore_sticky_posts' => 1,
        'post__not_in' => array($exclude_featured)
    )
);
query_posts( $args );
while ($wp_query->have_posts()) : $wp_query->the_post(); 
    if ( $exclude_featured == get_the_ID() )
        continue;
        echo the_title();
        echo '<br />';
endwhile; 

Thanks for any help guys.

3 Answers 3

1

you could use wp_list_pluck();

if ( $exclude_featured )
    $args['post__not_in'] = wp_list_pluck( $exclude_featured->posts, 'ID' );
    $args['posts_per_page'] = 999;
    query_posts( $args );
endif;
while ( have_posts() ) : the_post();
...
2
  • Can you describe what wp_list_pluck does for users that might not know? :) Commented Oct 21, 2012 at 2:51
  • 1
    Here is the reference in the wordpress codex codex.wordpress.org/Function_Reference/wp_list_pluck It's a relative new function and I only learned of it because a premium theme I know is using it for this purpose ;)
    – mirage
    Commented Oct 21, 2012 at 8:41
2

Here is the really simple way to do it.

$args = array(
          'posts_per_page' => -1,
          'category__in' => 3,
          'ignore_sticky_posts' => 0
       );

$my_custom_query = new WP_Query( $args );

while ( $my_custom_query->have_posts() ) :$my_custom_query->the_post();

<div id="post-<?php the_ID(); ?>" <?php post_class(); ?>>
//Your Stuff

endwhile;

// Reset Post Data
wp_reset_postdata();
  • No need to use global $wp_query; especially 2x.
  • No need to echo br tags...several times if your using the <?php post_class(); Use CSS, that way you will automatically get a class called .sticky !
  • Use a better name for your query.

Here is an update using 2 queries to address the details I missed, since it will be somewhat safe.

// **Loop 1** get the first sticky only 

$sticky = get_option( 'sticky_posts' );

$the_query = new WP_Query( 'p=' . $sticky[0]);

while ( $the_query->have_posts() ) : $the_query->the_post(); ?>

<div id="post-<?php the_ID(); ?>" <?php post_class(); ?>>

// your content

<?php endwhile; wp_reset_postdata(); ?>

//////////////
//**Loop 2** exclude the sticky from the Loop 1

$args = array(
        'posts_per_page' => -1,
        'ignore_sticky_posts' => 1,
        'post__not_in' => array($sticky[0])

);

$super_query = new WP_Query($args);

while ( $super_query->have_posts() ) : $super_query->the_post(); ?>

<div id="post-<?php the_ID(); ?>" <?php post_class(); ?>>

// your content

<?php endwhile; wp_reset_postdata(); ?>
4
  • You're missing a closing bracket from your array, otherwise this works. However not as required. This stacks all sticky posts at the top of the loop. What I want is max of 1 stickypost at the top of the loop (the latest by date) regardless of how many are stickied, and then for subsequent results it should fall back into its place by date regardless of being a sticky or not. You're close, but I already had similar before I posted the question. Do you think you could possibly still do it with one loop? Thanks.
    – davebowker
    Commented Oct 20, 2012 at 23:11
  • I've actually just noticed that if you specifically set the category you want it no longer outputs sticky posts at the top.
    – davebowker
    Commented Oct 20, 2012 at 23:56
  • Sticky posts do not work for archive pages, for instance a custom category template when you query for the category, sticky means semantically something in the front page. To highlight posts for archive pages and such just create a category or taxonomy. To answer the 1 sticky question I would look ar this wordpress.stackexchange.com/questions/6493/… or Mridul Aggarwal answer in this thread. Also the other answer using wp_list_pluck for 1 query, its hard to say which is the fastest.
    – Wyck
    Commented Oct 21, 2012 at 1:56
  • I updated my answer with an example of how to do it using 2 query's.
    – Wyck
    Commented Oct 21, 2012 at 2:53
1

I see 2 major problems. a) You shouldn't modify a global variable directly & b) You shouldn't use query_posts. Here's a reworked example

functions.php

add_action('pre_get_posts', 'customize_query');
function customize_query($query) {
    if(!$query->is_main_query() || !is_page_template('template-file-name.php'))
        return;

    $wp_query = new WP_Query(array(
        'post_type' => 'post',
        'posts_per_page' => 1,
        'category__in' => 3,
        'post__in'  => get_option( 'sticky_posts' )
    ));

    $query->set('posts_per_page', -1);
    $query->set('ignore_sticky_posts', 1);
    if(!empty($wp_query->posts))
        $query->set('post__not_in', array($wp_query->posts[0]->ID));

}

in the template file

$query = new WP_Query(array(
    'post_type' => 'post',
    'posts_per_page' => 1,
    'category__in' => 3,
    'post__in'  => get_option( 'sticky_posts' )
));
while ($query->have_posts()) : $query->the_post();
    echo the_title();
    echo '<br />';
endwhile; 

echo '<br />';
echo '<br />';

while (have_posts()) : the_post(); 
        echo the_title();
        echo '<br />';
endwhile;
7
  • Trying this, but it's throwing an error: Fatal error: Call to a member function have_posts() on a non-object. I tried to debug but I'm not really familiar enough with pre_get_posts
    – davebowker
    Commented Oct 20, 2012 at 23:12
  • Did you put the correct template filename here !is_page_template('template-file-name.php')? Commented Oct 20, 2012 at 23:22
  • Yes, and I saw you just made an edit so I tried your new code and it's still throwing the same error.
    – davebowker
    Commented Oct 20, 2012 at 23:24
  • i removed the caching functions, can you see once again Commented Oct 20, 2012 at 23:30
  • Error fixed now, but the second while loop is picking up the default wp_query of the page. EG, it gets the sticky in the first loop, then the page title in the second, not the list of results in the same query that aren't stickied.
    – davebowker
    Commented Oct 20, 2012 at 23:35

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.