Steve Taylor photo

Get a number of posts with optional sticky posts at the top

I’ve not really used sticky posts in WordPress. I’ve just been hacking around to try and use this system instead of my own custom field checkbox for “featuring” posts. There’s some definite pros: the user can toggle the status via the Quick Edit feature instead of having to edit the whole post. There’s also some cons: the sticky posts system has some quirks which, while understandable, limit it slightly.

One niggle is that sticky posts in standard post loops only really impact things (i.e. bubble to the top) when is_home() is true. The real killer is that when you’re only outputting a certain number of posts, using, say, get_posts and the numberposts argument, if there are sticky posts, you’ll get the sticky posts plus the number of posts you specify from the rest of the posts. So if you ask for 3 posts and there’s 1 sticky post, you’ll get 4 posts in total. Grrr!

This is acknowledged in a couple of trac tickets. There are reasons for this—it’s a feature, not a bug, as they say—but what do you do if want to do things your way?

Try this function in your custom theme:

function slt_getPostsWithSticky( $total = 3, $category = 0 ) {
	// Initialize
	$sticky = get_option( 'sticky_posts' );
	$args = array( 'numberposts' => $total, 'post__in' => $sticky );
	if ( $category ) $args[ 'category' ] = $category;
	// Get sticky posts
	$posts = get_posts( $args );
	// Are there enough posts?
	if ( count( $posts ) < $total ) {
		// Revise arguments to get remaining non-sticky posts
		$nonstickyTotal = $total - count( $posts );
		unset( $args[ 'post__in' ] );
		$args[ 'post__not_in' ] = $sticky;
		$args[ 'numberposts' ] = $nonstickyTotal;
		$posts = array_merge( $posts, get_posts( $args ) );
	}
	return $posts;
}

UPDATE 6/10/10: The above code now allows for situations where there are more sticky posts than specified in the $total parameter.

6 comments

  1. Jorge avatar Jorge

    Hi Steve,

    Just wanted to let you know that this works GREAT!

    I used it to create a simple post-based banner management system for a client site :)

    Thank You!

  2. Jorge avatar Jorge

    Hi Steve,

    After using this for a few days, I’ve noticed a strange behavior:

    If there are no sticky posts, this will list all the latest posts limited by the the number of posts set in the “settings” page in the admin area.

    So if I have no sticky posts the If statement is completely disregarded.

    Any ideas?

    If I find a solution I’ll post it here.

    Thanks!

  3. Jorge avatar Jorge

    Ok – I think I’ve fixed my problem. This is what I did:


    function get_banners($total, $category) {
    // Initialize
    $sticky = get_option( 'sticky_posts' );
    $args = array( 'post__in' => $sticky );
    $args[ 'orderby' ] = 'rand';
    $args[ 'numberposts' ] = $total;

    if ( $category ) $args[ 'category' ] = $category;
    // Get sticky posts
    $posts = get_posts( $args );
    // Are there enough posts?
    if ( count( $posts ) < $total ) {
    // Revise arguments to get remaining non-sticky posts
    $nonstickyTotal = $total - count( $posts );
    unset( $args[ 'post__in' ] );
    unset($args[ 'numberposts' ]);
    $args[ 'post__not_in' ] = $sticky;
    $args[ 'numberposts' ] = $nonstickyTotal;

    $posts = array_merge( $posts, get_posts( $args ) );
    }
    return $posts;
    }

    If you find any problems with this code, please let me know.

    Thanks again!

  4. Thanks for the heads-up Jorge, but I’m a little confused on this. I’ve just done some testing, and revised the code. The only problem I could see in the original code was that it didn’t limit the number of sticky posts returned in the first get_posts. I’ve added that now, and my testing shows it to work fine with no sticky posts, a few sticky posts, and with a greater number of sticky posts that specified in the $total parameter.

    If there are no sticky posts, the first get_posts will return nothing. Then, the if statement will kick in and get $total posts.

    The total number of posts set in Settings > Reading in WordPress admin shouldn’t affect the above code at all, because this uses get_posts, not the standard Loop. I think what was happening before was that, if there were 10 sticky posts, and you called this function to get 8 posts, it would actually have returned the first 5 sticky posts. This is because the numberposts argument of get_posts defaults to 5.

    Anyway, let me know if you spot any other problems…

  5. Jorge avatar Jorge

    No problem Steve.

    I tested your code with WP 3.0.1 and this what happened:

    I was calling the function asking for 3 total posts in a certain category. When I had 1 sticky post for example, there was no problem. The function would return the sticky and 2 more posts.
    But, if there where no sticky posts, I would get the latest 5 posts, from any category. I had the settings to list 5 posts max, so that’s why I believe it was related.

    Anyhow, I’m not a real php programmer, so I might have done something wrong. Though now it’s working as I need to.

    Thanks again!

    Cheers!

Leave a comment

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>