Sticky posts on home page, search, tag and archives without plugin

I have a question today. I am looking for a solution to make sticky post available on my website. But, i just got it work on homepage with the code below.

function wpb_latest_sticky() { 

/* Get all sticky posts */
$sticky = get_option( 'sticky_posts' );

/* Sort the stickies with the newest ones at the top */
rsort( $sticky );

/* Get the 5 newest stickies (change 5 for a different number) */
$sticky = array_slice( $sticky, 0, 5 );

/* Query sticky posts */
$the_query = new WP_Query( array( 'post__in' => $sticky, 'ignore_sticky_posts' => 1 ) );
// The Loop
if ( $the_query->have_posts() ) {
    $return .= '<ul>';
    while ( $the_query->have_posts() ) {
        $the_query->the_post();
        $return .= '<li><a href="' .get_permalink(). '" rel="nofollow noreferrer noopener" title="'  . get_the_title() . '">' . get_the_title() . '</a><br />' . get_the_excerpt(). '</li>';

    }
    $return .= '</ul>';

} else {
    // no posts found
}
/* Restore original Post Data */
wp_reset_postdata();

return $return; 

} 
add_shortcode('latest_stickies', 'wpb_latest_sticky');

I was thinking to make the sticky post to be displayed on search, tag, and category as well.

Any solution? Thanks!

Answers:

Thank you for visiting the Q&A section on Magenaut. Please note that all the answers may not help you solve the issue immediately. So please treat them as advisements. If you found the post helpful (or not), leave a comment & I’ll get back to you as soon as possible.

Method 1

This same exact question was asked earlier this week or over the weekend, and it had me thinking. Here is the idea that I came up with.

If you look at the source code of the WP_Query class, you will see that sticky posts is only added to the first page of the home page. There is also no filter supplied to change this behavior in order to set the required templates according to your liking.

There are many ways to display sticky posts on other templates through widgets, custom queries, shortcodes (which I will not recommend due to the fact that using do_shortcode() is slower that using the function itself) or custom functions where you need to display them. I have opted to go with using the_posts filter and pre_get_posts action.

HERE’S HOW:

  • Get the post ID’s saved as sticky posts with get_option( 'sticky_posts' )
  • Remove this posts from the main query with pre_get_posts. As stickies are included on the home page, we will exclude the home page. We will also exclude normal pages
  • Inside a custom function, run a custom query with get_posts to get the posts set as sticky posts.
  • Merge the returned array of sticky posts with the current returned posts of the main query with array_merge
  • Hook this custom function to the_posts filter

Here is the code: (Requires PHP 5.4+)

add_action( 'pre_get_posts', function ( $q )
{
    if (    !is_admin()
         && $q->is_main_query()
         && !$q->is_home()
         && !$q->is_page()
    ) {

        $q->set( 'post__not_in', get_option( 'sticky_posts' ) );

        if ( !$q->is_paged() ) {
            add_filter( 'the_posts', function ( $posts )
            {
                $stickies = get_posts( ['post__in' => get_option( 'sticky_posts' ), 'nopaging' => true] );

                $posts = array_merge( $stickies, $posts );

                return $posts;

            }, 10, 2);
        }

    }
});

Method 2

Pieter’s answer does work, but it displays all sticky posts even on pages when we want to display filtered posts (to a given category for instance).

The following works for me. Adding this to functions.php only displays stickies within the subset of filtered posts :

add_filter('the_posts', 'bump_sticky_posts_to_top');
function bump_sticky_posts_to_top($posts) {
    $stickies = array();
    foreach($posts as $i => $post) {
        if(is_sticky($post->ID)) {
            $stickies[] = $post;
            unset($posts[$i]);
        }
    }
    return array_merge($stickies, $posts);

(Credits : http://pastebin.com/Y5jVrKg4, with a slight change to prevent a Warning: array_merge() [function.array-merge]: Argument #1 is not an array on line 9 error)

However, there is a big flaw. It will not promote a sticky post on top if this sticky post in not to be present in the current page.

A system that would set the .sticky CSS class to the sticky posts would also be nice.

Method 3

I needed some modifications(original see Pieters answer) to get it to work just on category pages. This solution will display all sticky posts of the current category (and child categories), but not on search, tag and single pages:

add_action( 'pre_get_posts', function ( $q ) {
    if ( !is_admin()
         && $q->is_main_query()
         && !$q->is_home()
         && !$q->is_tag()
         && !$q->is_search()
         && !$q->is_page()
         && !$q->is_single()
    ) {

      function is_existing_cat($q_catName) {
           $existingCats = get_categories();
           $isExistingCat = false;

           foreach ($existingCats as $cat) {
                if ($cat->name == $q_catName) {
                     $isExistingCat = true;
                }
           }

           return $isExistingCat;
      }

        if ($q->is_category() && !$q->is_paged() && is_existing_cat($q->query['category_name'])) {

            $q->set( 'post__not_in', get_option( 'sticky_posts' ) );

            add_filter( 'the_posts', function ( $posts ) {
                $catName = get_category(get_query_var('cat'))->name;
                $catID = get_cat_ID($catName);

                if ( !empty(get_option( 'sticky_posts' )) ) {
                    $stickies = get_posts( [
                        'category' => $catID,
                        'post__in' => get_option( 'sticky_posts' )
                    ] );

                    $posts = array_merge( $stickies, $posts );
                }

                return $posts;

            }, 10, 2);
        }
    }
});

I added a function to check if the category is an existing category because I run into the issue that I was directed to the category.php instead of the expected 404.php.
Links like /existing-cat/not-existing-post worked but links like /not-existing-post-or-page-or-cat didn’t.

If you need a css class on that post you add this code to the PHP part of the template that displays the content of the post (in twentysixteen for example: template-parts/content.php)

<?php
if (is_sticky()) {
    $stickyClass = 'sticky';
} else {
    $stickyClass = '';
}
?>

Than you add this class to the css classes of the post post_class($stickyClass); (this function will add the class attribute to the HTML element with a few classes and your added class). You may find this function already located at the article:

<article id="post-<?php the_ID(); ?>" <?php post_class($stickyClass); ?>>
    ...
</article><!-- #post-## -->


All methods was sourced from stackoverflow.com or stackexchange.com, is licensed under cc by-sa 2.5, cc by-sa 3.0 and cc by-sa 4.0

0 0 votes
Article Rating
Subscribe
Notify of
guest

0 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
0
Would love your thoughts, please comment.x
()
x