Frage

I have a partial that I call around the website at various points. This partial simply displays the latest Post.

It looks like so:

<?php
$args = array(
    'posts_per_page'    => 1,
    'order'             => 'desc'
);
query_posts($args);

if ( have_posts() ) :
    while ( have_posts() ) : the_post();
        if (has_post_thumbnail( $post->ID ) ){
            $featured_image = wp_get_attachment_image_src( get_post_thumbnail_id( $post->ID ), 'home-news-thumbnail' );
        }
        ?>
        <a href="<?php echo get_the_permalink(); ?>"><h3><?php echo get_the_title(); ?></h3></a>
        <?php if(!empty($featured_image)): ?>
        <a href="<?php echo get_the_permalink(); ?>">
            <img src="<?php echo $featured_image[0]; ?>" alt="" width="250" class="pull-left">
        </a>
        <?php endif; ?>
        <p>
            <?php echo get_the_excerpt(); ?>
        </p>
        <a href="<?php echo get_the_permalink(); ?>" class="btn btn-brand-dark">more</a>
        <div class="clearfix"></div>
<?php
    endwhile;
endif;

wp_reset_postdata();
?>

This works fine until I 'Sticky' a post. Then I get 2 posts instead of 1.

How can I amend all queries so that posts_per_page has the requested number of posts, regardless of sticky posts?

So in the example above, I'm currently getting 2 posts (despite requesting 1), but I want the latest post, whether that's a sticky post or not.

I know about ignore_sticky_posts parameter, but that will ignore the sticky post, which I don't want to do. If there's a sticky post it should be first.

War es hilfreich?

Lösung

Here's a way to force the exact posts_per_page value in WP_Query, regardless of sticky posts or custom post injects:

$args = [
    'posts_per_page'        => 1,
    '_exact_posts_per_page' => true   // <-- our custom input argument
];

by using our custom _exact_posts_per_page input argument.

I'm sure this has been implemented many times before, but I didn't find it at the moment, so let us try to implement it with this demo plugin:

<?php
/**
 *  Plugin Name:   Exact Posts Per Pages 
 *  Description:   Activated through the '_exact_posts_per_page' bool argument of WP_Query 
 *  Plugin URI:    http://wordpress.stackexchange.com/a/257523/26350 
 */

add_filter( 'the_posts', function( $posts, \WP_Query $q )
{
    if( 
        wp_validate_boolean( $q->get( '_exact_posts_per_page' ) )
        && ! empty( $posts ) 
        && is_int( $q->get( 'posts_per_page' ) ) 
    )
        $posts = array_slice( $posts, 0, absint( $q->get( 'posts_per_page' ) ) );

    return $posts;

}, 999, 2 ); // <-- some late priority here!

Here we use the the_posts filter to slice the array, no matter if it contains sticky posts or not.

The the_posts filter is not applied if the suppress_filters input argument of WP_Query is true.

Note that query_posts() is not recommended in plugins or themes. Check out the many warnings in the Code reference here.

Hope you can test it further and adjust to your needs!

Andere Tipps

If you want your query to ignore if a post is sticky and dont have it at the top of your ordered query:

//$args = array(
//    'posts_per_page' => 1,
//    'order'          => 'desc'
//    'ignore_sticky_posts' => 1 //set this
//);

with this the sticky posts will not be ignored, what will be ignored is their status of sticky, the sticky posts will still be in your result, what 'ignore_sticky_posts' does is tell the query to not bring the posts that are sticky to the top if you are ordering them.

EDIT:

If you want just 1, remove the While loop logic:

<?php
$args = array(
    'posts_per_page'    => 1,
    'order'             => 'desc'
);
query_posts($args);

if ( have_posts() ) :

        if (has_post_thumbnail( $post->ID ) ){
            $featured_image = wp_get_attachment_image_src( get_post_thumbnail_id( $post->ID ), 'home-news-thumbnail' );
        }
        ?>
        <a href="<?php echo get_the_permalink(); ?>"><h3><?php echo get_the_title(); ?></h3></a>
        <?php if(!empty($featured_image)): ?>
        <a href="<?php echo get_the_permalink(); ?>">
            <img src="<?php echo $featured_image[0]; ?>" alt="" width="250" class="pull-left">
        </a>
        <?php endif; ?>
        <p>
            <?php echo get_the_excerpt(); ?>
        </p>
        <a href="<?php echo get_the_permalink(); ?>" class="btn btn-brand-dark">more</a>
        <div class="clearfix"></div>
<?php

endif;

wp_reset_postdata();
?>

you can even remove the 'posts_per_page' => 1 the output will be the first one.

Try using if(! is_sticky())

<?php
$args = array(
    'posts_per_page'    => 1,
    'order'             => 'desc'
);
query_posts($args);

if ( have_posts() ) :
    while ( have_posts() ) : the_post();
    if(! is_sticky()) {
        if (has_post_thumbnail( $post->ID ) ){
            $featured_image = wp_get_attachment_image_src( get_post_thumbnail_id( $post->ID ), 'home-news-thumbnail' );
        }
        ?>
        <a href="<?php echo get_the_permalink(); ?>"><h3><?php echo get_the_title(); ?></h3></a>
        <?php if(!empty($featured_image)): ?>
        <a href="<?php echo get_the_permalink(); ?>">
            <img src="<?php echo $featured_image[0]; ?>" alt="" width="250" class="pull-left">
        </a>
        <?php endif; ?>
        <p>
            <?php echo get_the_excerpt(); ?>
        </p>
        <a href="<?php echo get_the_permalink(); ?>" class="btn btn-brand-dark">more</a>
        <div class="clearfix"></div>
<?php } 
    endwhile;
endif;

wp_reset_postdata();
?>

Please note, this will completely prevent any sticky posts from appearing.

I just noticed your comment that you wanted to accomplish this via a filter, so here you go (this basically expounds on the comments I've made on your original question and @DavidLee's answer).

The following will result in 1 and only 1 post, regardless of stickies. If the most recently published post is sticky, you will get it. If the most recently published post is not sticky, you will still get it. And, you will get no other posts, sticky or otherwise.

add_filter ('pre_get_posts', 'get_only_one_post') ;

function
get_only_one_post ($wp_query)
{
    if (is_admin () || !$wp_query->is_main_query ()) {
        // we're not in the Main Loop, so return the query unmodified
        return ($wp_query) ;
        }

    // order by most recently published 1st
    // this isn't really necesary (since it is the default),
    // but include it just in case some other filter has modified that default
    $wp_query->set ('orderby', 'date') ;
    $wp_query->set ('order', 'DESC') ;

    // limit to 1 post, not counting stickies
    $wp_query->set ('posts_per_page', 1) ;

    // disable the special processing of sticky posts.
    // without this, ALL sticky posts will be returned, whether they match
    // the rest of the query_vars or not
    $wp_query->set ('ignore_sticky_posts', true) ;

    return ($wp_query) ;
}

Explanation

Without 'ignore_sticky_posts' => true

The following is done:

  1. retrieve posts according to query vars, including respecting posts_per_page and orderby
    • any sticky posts that match query vars will already be included in the results
  2. move sticky posts that are included in the results to the front of the list
  3. retreive all sticky posts
    1. those not already in the results are preprend to the list

With 'ignore_sticky_posts' => true

The following is done:

  1. retrieve posts according to query vars, including respecting posts_per_page and orderby
    • any sticky posts that match query vars will already be included in the results

Edit: Additional answer

OK, try this:

add_filter ('pre_get_posts', 'set_posts_per_page_on_front_end_main_query') ;
add_filter ('the_posts', 'respect_posts_per_page') ;

function
respect_posts_per_page ($posts, $wp_query)
{
    return (array_slice ($posts, 0, $wp_query->get ('posts_per_page', count ($posts)))) ;
}

function
set_posts_per_page_on_front_end_main_query ($wp_query)
{
    if ( is_admin () || !$wp_query->is_main_query ()) {
        return ($wp_query) ;
        }

    $wp_query->set ('posts_per_page', 1) ;

    return ($wp_query) ;
}
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit wordpress.stackexchange
scroll top