1

I'm using ACF to create a "Featured" post section of the index.php and I'm having trouble then excluding that post from the main query.

        <?php 

        $tmp = $posts;

        $isPaged = (get_query_var('paged') == 0) ? false : true;

        $posts = get_posts(array(
            'numberposts'   => 1,
            'post_type'     => 'post',
            'meta_key'      => 'featured_post',
            'meta_value'    => true
        ));

        if( !$isPaged && !is_archive() && $posts ): ?>

            <?php foreach( $posts as $post ): setup_postdata( $post ) ?>

                <?php $excludeID = $post->ID; ?>

                ... html stuff ...

            <?php endforeach; ?>

            <?php wp_reset_postdata(); ?>

        <?php endif; ?>

I then have the $excludeID with the ID of the post I need to filter out, however, I don't know how to add a posts_not_in to the main query. If this was a custom query I'd have no problem, but I'd like to leave it as the default query if possible. I tried doing this with pre_get_posts but the way I tried is clearly not the right way as there was a memory error.

Is there some sort of thing like pre_get_posts I can use before the main query?

I can't just filter out featured posts using pre_get as I only want it to show the most recent featured post, and all others just display as normal in the main query. I also can't just do and if post id == $excludeID because it messes up the posts per page. I'm sure this is possible it's just not something I've come across yet :)

Thanks.

1 Answer 1

2

I think the cleanest method here is just run a custom query, get the ID and then remove that post from the main query with post__not_in. You can additionally run a query inside a function and then call the function in your featured section and in pre_get_posts. There is another way, using a global to pass the ID from your custom query to your main query, but you should never go this route. You must avoid creating globals at all cost, they are evil

Just a note on globals and variables, avoid using $posts, $post and $wp_query as variables and assinging custom values to it. These three are globals variables used by Wordpress whichholds the main query object and post objects from the main query. Assigning any other custom value to it breaks these globals. The only time you should use any of these globals is when you use get_posts() with setup_postdata(). setup_postdata() only works with the $post global, any other global name will not work. That is why you should use wp_reset_postdata() after your foreach loop when you used setup_postdata() to reset the $post global to the last post in the main query

Example:

$q = get_posts( 'posts_per_page=1' );
if ( $q ) {
    foreach ( $q as $post ) {
       setup_postdata( $post );
       // Run loop as normal with template tags
    }
    wp_reset_postdata();
}

Now, lets look at the real problem at hand. What we will do here is create a function which we can use in our featured section and pre_get_posts.

(The following code is untested and requires PHP 5.4+. Feel free to modify as needed)

function get_featured_custom_post( $main_query = false )
{
    /*
     * For the purpose of performance, I have created a variable called $main_query with a value of false
     * When we run this function in pre_get_posts, we just need the post id, nothing more. For this, our argumaents will change
     * The default is meant to return the complete post object, that is for the featured section
     */
    if ( $main_query == true ) {
        $fields = ['fields' => 'ids'];
    } else { 
        $fields = ['fields' => 'all'];
    }

    // Build our query arguments
    $defaults = [ 
        'numberposts'   => 1,
        'post_type'     => 'post',
        'meta_key'      => 'featured_post',
        'meta_value'    => true
    ];
    $args = wp_parse_args( $defaults, $fields ); 

    $q = get_posts( $args );

    return $q;
}

The value from our function get_featured_custom_post will either hold an array with the featured post id or the complete post object. Now, lets put that to use

IN pre_get_posts

add_action( 'pre_get_posts', function ( $q ) 
{
    if (     $q->is_home() // Targets the home page only
         && $q->is_main_query() // Targts the main query only
    ) {
        // Get the post to exclude, remeber, we need id only, so set $main_query to true
        $exclude = get_featured_custom_post( true );
        if ( $exclude ) // Only set post__not_in if we have a post to exclude, otherwise the result might be unexpected
            $q->set( 'post__not_in', (array) $exclude );
    }
});

FEATURED SECTION

In our featured section we can use the function as follow

$q = get_featured_custom_post(); // We need the complete post object, so leave $main_query at default which is false
if ( $q ) {
    foreach ( $q as $post ) {
       setup_postdata( $post );
       // Run loop as normal with template tags
    }
    wp_reset_postdata();
}
3
  • As a lowly front end developer, I'm not exactly sure what's going on, but it works like a charm! Can you tell our php dev is on annual leave yet :) Thanks again Pieter, you've been awesome.
    – evu
    Commented Jun 25, 2015 at 9:53
  • Oh and thanks for the tips about the variables, I did in fact try and run the query in pre get posts and had a memory error, and I also tried (and failed) with super globals hehe.
    – evu
    Commented Jun 25, 2015 at 9:54
  • Hahaha, it is always a pleasure to help others. Note, I also have tons to learn. Learning every day ;-). Enjoy Commented Jun 25, 2015 at 9:56

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.