0

This is the code I've been using, with the preceding query parameters excluded for brevity. As you can see, this comes from a widget that displays a grid of posts. The problem is that I can't figure out how to write a query, or multiple queries, that will retrieve the latest post from each of the six categories in a way that works.

    $r = new WP_Query( apply_filters( 'widget_posts_grid', $query ) );

    if ( $r->have_posts() ) :
    ?>
    <?php echo wp_kses_post( $args['before_widget'] ); ?>
    <?php
    if ( $title ) {
        echo wp_kses_post( $args['before_title'] . $title . $args['after_title'] );
    }
    ?>
    <div class="posts-grid">
        <div class="row">
        <?php while ( $r->have_posts() ) : $r->the_post(); ?>
            <div class="col-md-4 col-sm-6">
            <?php get_template_part( 'content', get_post_format() ); ?>
            </div>
        <?php endwhile; ?>
        </div>
    </div>
    <?php echo wp_kses_post( $args['after_widget'] ); ?>
    <?php
    wp_reset_postdata();

    endif;

I have tried a foreach loop with array_merge, but the result apparently can't be looped through...

$do_not_duplicate = array();
$r = new WP_Query();
$categories = get_categories(); 
    foreach ( $categories as $category ) {
    $args = array(
    'cat' => $category->term_id,
    'post_type' => 'post',
    'posts_per_page' => '1',
    'post__not_in' => $do_not_duplicate
    );

    $little_array = new WP_Query( $args );
    $r = array_merge( $r, $little_array->posts );

    while ( $r->have_posts() ) { $r->the_post();
    $do_not_duplicate[] = $post->ID;
    }
    }
$r->post_count = count( $r->posts );

for($i = 1; $r->have_posts(); $i++) {                          
    $r->the_post();
 }
        if ( $r->have_posts() ) :

From my research, I really thought this would work. However, it seems that after $r = array_merge( $r, $little_array->posts );, $r comes up empty. print_r($r); prints nothing.

I appreciate the help.


For reference, this is the code that follows the above query:

if ( $r->have_posts() ) :
?>
<?php echo wp_kses_post( $args['before_widget'] ); ?>
<?php
if ( $title ) {
    echo wp_kses_post( $args['before_title'] . $title . $args['after_title'] );
}
?>
<div class="posts-grid">
    <div class="row">
    <?php while ( $r->have_posts() ) : $r->the_post(); ?>
        <div class="col-md-4 col-sm-6">
        <?php get_template_part( 'content', get_post_format() ); ?>
        </div>
    <?php endwhile; ?>
    </div>
</div>
<?php echo wp_kses_post( $args['after_widget'] ); ?>
<?php
wp_reset_postdata();

endif;
3
  • 1
    Why do you want to "resemble" multiple WP_Query into one? Can you explain it? Why it doesn't work? I think you forgot to explain it.
    – cybmeta
    Commented Dec 7, 2015 at 14:19
  • @cybmeta, Edited.
    – David
    Commented Dec 7, 2015 at 14:46
  • This has probably been answered here or here
    – s_ha_dum
    Commented Dec 7, 2015 at 14:56

1 Answer 1

2

I cannot really see doing this than to run a couple of queries here, one per category. We will need to be clever here to avoid a lot of unnecessary work.

Lets look at the code; (which I will comment to ease the process of understanding)

// Get the categories. We will only get the category ID's to speed things up
$category_args = [
    'fields' => 'ids',
];
$categories = get_categories ( $category_args );

// Check if we have categories to avoid bugs
if ( $categories ) {

    // Define the variable to hold an array of posts not to duplicate
    $do_not_duplicate = [];
    // Define a variable to hold our posts
    $posts_array      = [];

    foreach ( $categories as $cat_id ) {

        // Setup our query arguments to get our posts
        $args = [
            'cat'            => $cat_id,
            'posts_per_page' => 1,
            'post__not_in'   => $do_not_duplicate,
            'fields'         => 'ids' // Only get post id's to increase performance
        ]; 
        /** 
         * Lets use get_posts as we do not need the whole object and get_posts by default
         * legally breaks pagination which makes the query faster, and it automatically 
         * ignore sticky posts and by default does not get modified by filters
         */
        $q = get_posts( $args );
        // Check if we have posts
        if ( $q ) {
            /**
             * Now we need to add the post ID to the $do_not_duplicate array.
             * We will also pass the posts in $q to $posts_array
             * NOTE: you will need to rework this if you ever need more than one post per category
             */
            $do_not_duplicate[] = $q[0];
            $posts_array[]      = $q[0];
        }
    } //endforeach $categories

    // We can now run an instance of WP_Query to get the posts and query object
    if ( $posts_array ) {
        $final_args = [
            'posts_per_page'      => count( $posts_array ),
            'post__in'            => $posts_array,
            'ignore_sticky_posts' => 1, // Ignore stickies
            'no_found_rows'       => true, // Skip pagination, remove if needed
        ];
        $final_query = new WP_Query( $final_args );
        var_dump( $final_query );
        // Now we can run te loop and output our posts
        if ( $final_query->have_posts() ) {
            while ( $final_query->have_posts() ) {
                $final_query->the_post();

                the_title();

            } //endwhile
            wp_reset_postdata(); // NEVER EVER forget this line
        } // endif $final_query->have_posts()
    } // endif $posts_array 
} // endif $categories

You can adjust, extend and modify it to suit your needs. Note, everything in the code has been setup to accommodate for one post per category only. You will need to alter how $do_not_duplicate and $posts_array are build if you are going to query more than one post per category

5
  • Thank you. That seems to work, but the resulting array is not a WP_Query Object, which breaks the functionality of the code that follows. Is it possible to convert the result into a WP_Query Object?
    – David
    Commented Dec 7, 2015 at 21:55
  • Additionally, this method is not excluding sticky posts.
    – David
    Commented Dec 8, 2015 at 2:03
  • 1
    Good morning, sorry for only coming back now as it was night time here :-). I have updated the code, so check it out. Hope that help Commented Dec 8, 2015 at 4:22
  • Great. Thank you very much. I had to modify a couple things, since I wanted to exclude the latest sticky post, but it worked out perfectly. :)
    – David
    Commented Dec 8, 2015 at 5:31
  • My pleasure, and feel free to extend and modify the code as needed to suite your needs. Glad it worked out. Enjoy ;-) Commented Dec 8, 2015 at 5:36

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.