Ask your WordPress questions! Pay money and get answers fast! Comodo Trusted Site Seal
Official PayPal Seal

Sticky posts before normal posts WITH PAGINATION WordPress

  • SOLVED

I've tried everything. I've looked at every. single. question. here, on StackOverflow, the WP help forum, googled 10 pages deep and literally tried EVERY combination of code I could find, for the last 2 days, and can't get anything to work how I want it. Surely it can't be impossible? The goal seems so simple!

<strong>THE GOAL</strong>: <em>Show ALL sticky posts first, and then normal posts after them, in order, and WITH PAGINATION.</em>

Example: With posts per page set to 10, having 15 sticky posts and 15 normal posts, page 1 would be 10 sticky posts, page 2 would be 5 sticky posts and then 5 normal posts, and page 3 would be 10 normal posts. Ordered by date.

I've tried multiple loops, various queries, and have come CLOSE but so far no cigar. Here's what I have so far:

<!-- THIS CODE QUERIES ALL POSTS AND RETURNS ONLY STICKY POSTS, DISPLAYED AT TOP OF THE PAGE -->
<?php
// show only ads within this specific category
$term = get_term_by('slug', get_query_var('term'), get_query_var('taxonomy'));
//$paged = (get_query_var('paged')) ? get_query_var('paged') : 1;
$sticky=get_option('sticky_posts');
$args=array(
'post_type' => 'my_custom_post_type',
'ad_cat' => $term->slug,
'caller_get_posts' => 1,
'post__in' => $sticky,
'posts_per_page' => -1
//'paged' => $paged
);
query_posts($args);
?>
<?php get_template_part( 'loop', 'post_featured' ); ?>
<?php wp_reset_query(); ?>

<!-- THIS CODE QUERIES ALL POSTS AND RETURNS ONLY REGULAR POSTS, DISPLAYED BELOW THE STICKIES -->
<?php
// show only ads within this specific category
$term = get_term_by('slug', get_query_var('term'), get_query_var('taxonomy'));
$paged = (get_query_var('paged')) ? get_query_var('paged') : 1;
$sticky=get_option('sticky_posts');
$args=array(
'post_type' => 'my_custom_post_type',
'ad_cat' => $term->slug,
'caller_get_posts' => 1,
'post__not_in' => $sticky,
'paged' => $paged,
);
query_posts($args);
?>
<?php get_template_part( 'loop', 'post_normal' ); ?>
<?php wp_reset_query(); ?>


The above shows ALL sticky posts and then (5) normal posts. This is the closest I have gotten, but #1, I don't want to show ALL sticky posts on the first page, I want to adhere to the pagination rules. #2, ALL sticky posts show up on all paged pages. So if I have 50 sticky posts and 50 normal posts, each page shows 55 posts - the first 50 are the sticky posts and the last 5 are normal, which is not my goal (see THE GOAL above).

Is there an easy modification to one or both loops? Should I be using a double loop or is there another/better option to do this? I'm open to suggestions as long as I get it to WORK. I should note: I'm using WP 3.2.1 and a slimmed down non-plugin pagination function which was taken from WP-PageNavi (full pagination function can be seen here: http://paste2.org/p/1596821).

Also important to note is that this theme uses a custom function to allow Custom Post Types to use the Sticky Post feature. I believe that if I can get the above goal to work with regular posts and sticky posts, it will work in my situation.

Answers (4)

2011-08-24

Nick Parsons answers:

Hi Ramsey,

I spent some time this evening working out what I think might be the solution to your problem.

The code goes like this:
<?php
$paged = ($paged)? $paged : 1;
$posts_per_page = get_option('posts_per_page');
$sticky_posts = get_option('sticky_posts');
$num_sticky_pages = (count($sticky_posts) / $posts_per_page);

//If this page has only sticky posts
if( $paged <= $num_sticky_pages){
query_posts(array(
'post__in'=>$sticky_posts,
'paged'=>$paged,
'posts_per_page'=>$posts_per_page,
'ignore_sticky_posts'=>1
));
get_template_part( 'loop', 'post_featured' );
}
//If this page has a combination of sticky and regular posts
elseif( ($paged-1) < $num_sticky_pages && ($paged+1) > $num_sticky_pages ){
$num_regular_posts = ceil( (1-($num_sticky_pages+1-$paged)) * $posts_per_page );

query_posts(array(
'post__in'=>$sticky_posts,
'paged'=>$paged
));
get_template_part( 'loop', 'post_featured' );

query_posts(array(
'ignore_sticky_posts'=>1,
'paged'=>$paged - floor($num_sticky_pages),
'posts_per_page' => $num_regular_posts,
'post__not_in'=>$sticky_posts
));
get_template_part( 'loop', 'post_normal' );
}
//If this page has only regular posts
else{
$sticky_percent = ($sticky_percent = $num_sticky_pages - floor($num_sticky_pages))? $sticky_percent : 1;
$offset = (1-$sticky_percent) * $posts_per_page + ( ($paged-ceil($num_sticky_pages)-1)*$posts_per_page );

query_posts(array(
'post__not_in'=>$sticky_posts,
'offset'=>$offset
));
get_template_part( 'loop', 'post_normal' );

$lost_pages = (count(get_posts('caller_get_posts=1'))-1) / $posts_per_page - ($paged-ceil($num_sticky_pages));
$wp_query->max_num_pages += $lost_pages;
}
if(is_int($num_sticky_pages)){ $wp_query->max_num_pages += 1; }
?>



Hope that helps, let me know if you have any questions/issues with it!


Ramsey comments:

WOW Thank you Nick! Ok, so far, this seems to be the closest thing to what I want... but... I can't get it to work. First, this is a custom post type and this is the category, so I had to add a few lines to get it to show up my posts.

First, I added:
`$term = get_term_by('slug', get_query_var('term'), get_query_var('taxonomy'));`
to the top of the code.

Then, in each query_posts's array, I added:
`'post_type' => 'my_CPT',
'my_CPT_cat' => $term->slug, `

That got the posts queried and showing up. I can also change the # of posts per page via the Settings -> Reading page. But my pagination isn't showing up at all, and if I visit page/2/ I don't see any posts, as well as none of the "normal" posts seem to be showing up....


Ramsey comments:

I just figured out the reason nothing is showing up on page/2/ isn't because the pagination isn't working... it works. I changed the posts_per_page to 3 and pagination came up just fine and is working, but ONLY the FEATURED posts are coming up, so that's the reason it doesn't seem like the pagination was working.


Nick Parsons comments:

Glad to hear we're getting closer! I tested this and all was working fine with regular posts, so I'll bet it's the taxonomy/category that's throwing a wrench into the works somewhere.

Would you mind pasting your edited version of my code? Thanks!


Ramsey comments:

Thanks Nick. Hopefully it's something easy. Ok here's what I have:

<?php
$term = get_term_by('slug', get_query_var('term'), get_query_var('taxonomy'));
$paged = ($paged)? $paged : 1;
$posts_per_page = get_option('posts_per_page');
$sticky_posts = get_option('sticky_posts');
$num_sticky_pages = (count($sticky_posts) / $posts_per_page);

//If this page has only sticky posts
if( $paged <= $num_sticky_pages){
query_posts(array(
'post_type' => 'my_CPT',
'my_CPT_tax_cat' => $term->slug,
'post__in'=>$sticky_posts,
'paged'=>$paged,
'posts_per_page'=>$posts_per_page,
'ignore_sticky_posts'=>1
));
get_template_part( 'loop', 'post_featured' );
}

//If this page has a combination of sticky and regular posts
elseif( ($paged-1) < $num_sticky_pages && ($paged+1) > $num_sticky_pages ){
$num_regular_posts = ceil( (1-($num_sticky_pages+1-$paged)) * $posts_per_page );
query_posts(array(
'post_type' => 'my_CPT',
'my_CPT_tax_cat' => $term->slug,
'post__in'=>$sticky_posts,
'paged'=>$paged
));
get_template_part( 'loop', 'post_featured' );

query_posts(array(
'post_type' => 'my_CPT',
'my_CPT_tax_cat' => $term->slug,
'ignore_sticky_posts'=>1,
'paged'=>$paged - floor($num_sticky_pages),
'posts_per_page' => $num_regular_posts,
'post__not_in'=>$sticky_posts
));
get_template_part( 'loop', 'post_normal' );

}

//If this page has only regular posts

else{
$sticky_percent = ($sticky_percent = $num_sticky_pages - floor($num_sticky_pages))? $sticky_percent : 1;
$offset = (1-$sticky_percent) * $posts_per_page + ( ($paged-ceil($num_sticky_pages)-1)*$posts_per_page );

query_posts(array(
'post_type' => 'my_CPT',
'my_CPT_tax_cat' => $term->slug,
'post__not_in'=>$sticky_posts,
'offset'=>$offset
));
get_template_part( 'loop', 'post_normal' );

$lost_pages = (count(get_posts('caller_get_posts=1'))-1) / $posts_per_page - ($paged-ceil($num_sticky_pages));
$wp_query->max_num_pages += $lost_pages;
}

if(is_int($num_sticky_pages)){ $wp_query->max_num_pages += 1; }
?>


Nick Parsons comments:

Hmm, I'm still having trouble figuring out what's going on. Is it possible you could give me a temporary login to your test environment so that I can look at the same thing you're seeing?

Thanks!


Nick Parsons comments:

Actually, I may have figured it out. Just a minute and I'll post a solution for you to try.


Nick Parsons comments:

OK, no guarantees but I think what may be happening is that there's another post of a different type that's been marked sticky and it's upsetting the sticky post count.

I was able to fix it by replacing this line:
$sticky_posts = get_option('sticky_posts');
With this one:

$sticky_IDs = $wpdb->get_col("SELECT ID from ".$wpdb->prefix."posts WHERE post_type = 'my_cpt'");
$sticky_posts = array_intersect(get_option('sticky_posts'),$sticky_IDs);


It's possible there's something else causing the issue, but give that a try and let me know.


Ramsey comments:

Nope, looks like it's grabbing the sticky posts correctly as it displays the featured posts fine, including paginating them, but I see no 'normal' posts yet. This theme uses a Custom Post Type and a hack to activate the sticky feature for CPT's... I'm not exactly sure if this info helps, but here's the places in the theme for that:

// hack until WP supports custom post type sticky feature
function theme_sticky_option() {
global $post;

//if post is a custom post type and only during the first execution of the action quick_edit_custom_box
if ($post->post_type == 'my_CPT' && did_action('quick_edit_custom_box') === 1): ?>

<fieldset class="inline-edit-col-right">
<div class="inline-edit-col">
<label class="alignleft">
<input type="checkbox" name="sticky" value="sticky" />
<span class="checkbox-title"><?php _e('Featured Post'); ?></span>
</label>
</div>
</fieldset>
<?php
endif;
}
//add the sticky option to the quick edit area
add_action('quick_edit_custom_box', 'theme_sticky_option');



Nick Parsons comments:

Thanks for the info on the sticky hack, I figured you had something like that running.

Would you just try putting echo count($sticky_posts); in there and verify that the number echoed is the correct number of sticky posts in that type and taxonomy?


Nick Parsons comments:

I realized I wasn't super clear - by 'in there' I meant after the $sticky_posts variable is declared in the main template code.


Ramsey comments:

Looks like it is echoing 102 but I know that isn't correct. It is also echoing the very same number on EACH category (there are about 9) and I know for a fact that they don't all have the same number.

2011-08-22

Gabriel Reguly answers:

Hi Ramsey,

Where do you set posts per page?

Regards,
Gabriel


Ramsey comments:

Gabriel, I believe the normal posts are pulled from the general Settings -> Reading options. I can set them there, or via the custom loop, either way is fine with me.


Gabriel Reguly comments:

Hi Ramsey,

And what about the featured posts? Are they pulled from the same place?

Regards,
Gabriel


Ramsey comments:

Initially it was pulling the same # from the settings, so I would get 5 featured and 5 normal, and the pagination would show up under both, but when clicking on page 2, it wouldn't be the right results. And since I wanted ALL sticky posts up first, I set the posts_per_page on the first loop to be -1.


Gabriel Reguly comments:

Hi Ramsey,

Sorry but now I do not have the time to give you a working code, but my solution would be:

Use the first loop only if the shown post counter is less than the number of sticky posts -

Use the second loop when the post counter is greater than the number of sticky posts -

So you will need to add a post counter ( I am sure one already exists in WordPress ) and add some if statements.

If you could wait, tomorrow I will be able to show you some working code.

Regards,
Gabriel


Ramsey comments:

Gabriel, thank you. I will keep working on this but if I can't figure it out before you can provide a code example, please post when you can. Thanks.


Gabriel Reguly comments:

Hi Ramsey,

Looks like Nick Parsons coded a solution for you.

Regards,
Gabriel

2011-08-22

Denzel Chia answers:

Hi Ramsey,

If I am not wrong, what you need is a normal WordPress loop,
Just the normal.


<?php if ( have_posts() ) : while ( have_posts() ) : the_post(); ?>
<?php the_content(); ?>
<?php endwhile; endif; ?>


No need any custom query, by default WordPress will return all sticky posts first follow by normal posts. unless you need other condition, this should work.

Thanks.


Ramsey comments:

Denzel,

I am structuring the posts differently, via post templates, so I need to loop thru them twice in order to offer the different design layout.


Denzel Chia comments:

Hi,

You can avoid this by looping them once and push them all into arrays for display in the theme at different locations.

Example;

Put this at the very beginning of template, do not output anything from the loop, just assign get the content and push into content array. get the title and push into title array.


<?php

$content_array = array();
$title_array = array();

if ( have_posts() ) : while ( have_posts() ) : the_post();

$title_array[] = get_the_title($post->ID);
$content_array[] = get_the_content();

endwhile;
endif;

?>



At the theme location that you want to show your title and content, maybe first 3 posts only.

Do this,


<?php

for($i = 0 ; $i < 4; $++;){

echo $title_array[$i];
echo "<br/>";
echo $content_array[$];

}

?>



Hope this example if useful to you.

Thanks.


Ramsey comments:

Denzel, I'm getting a parse error: <em>Parse error: syntax error, unexpected T_INC, expecting T_VARIABLE or '$' </em> and the line in question is: for($i = 0 ; $i < 4; $++;) {


Ramsey comments:

Denzel, can you please expand on this a little more? I'm not sure if I'm doing it right. Thanks.


Denzel Chia comments:

Hi,

Sorry for late reply, we are on different time zone.

That is a typo error. Sorry! Missing letter i

should be


for($i = 0 ; $i < 4; $i++;) {


Thanks.


Ramsey comments:

Thanks Denzel, but no matter what combo I try, I still get some sort of fatal error. Could you possibly provide a more detailed example? I need to be able to style the posts differently (which I know can be done with the .sticky class) but also to include/exclude some variables and images. For example, the "featured" posts will have longer title chars, and include a special 'featured' badge image, and the "normal" posts have different extra meta keys and so forth.

2011-08-22

Julio Potier answers:

Hello

I already worked on the same problem as yours and i can tell you <strong>"it is impossible"</strong>.

How and why can i be so sure !?
-> Because sticky post are not design to be "merged" in the same request as "normal" posts.
So your problem can not be resolve with sticky post.

However, i found a way to do this with custom fields.

1) Add a new custom field in each post you need (in each actual sticky posts)
1.5) You can find you sticky posts in your DB with the query !
SELECT * FROM `wp_options` WHERE `option_name` = 'sticky_posts'
(this is a serialized data which contains all posts IDs)
2) In this field you can set a number (you can use 1 = sticky post and 0 or empty = not sticky) BUT you can also add another number like "10" or "150" to modify the posts priority now !
3) In your theme, where you need to "sort" your posts (tags, cat, search, index ...) add this line :
query_posts( $query_string . '&meta_key=priority&orderby=meta_value_num&order=DESC' );
before this one :
while (have_posts()) : the_post();

I think that's all.

You can contact me by PM if you want no problem.


Ramsey comments:

I can't really use custom fields because the website has over 200 existing posts and I don't want to have to go thru and add something to each post. I would also have to hack the front-end posting form to accept this new custom field, and further to hide it somehow so it's not evident to the user posting.


Julio Potier comments:

You (or we) can execute a sql query who can autoupdate your 200 posts.
Also, you (we) can hack the front-end to do this.

All is possible ;)

ps : in my example the custom field is named "priority", you can name it "_priority" if you want to hide it from the back-end. I can also add a meta-box with a great selectbox. This box allows you to select a priority for your posts ;)

See you soon