Merging a complex query with post_rewind and splitting posts into two columns

REWORKED APPROACH

Due to this becoming a bit of tameletjie due to styling issues, lets rework everything and look at a different approach

I think what we should do here is to rather shuffle the $posts array through usort() to get the posts sorted in the order we want, and then there after we can run the loop normally

Create our query:

<?php 
$args = array(
    'tax_query' => array(
        array(
            'taxonomy' => 'post-status',
            'field' => 'slug',
            'terms' => array ('post-status-published')
        )
    )
); 
$query = new WP_Query( $args ); 

Now that we have ran our query, we need to get the $posts property from $query and sort it. So this part will go below our query: (NOTE: This is completely untested)

if ( $query->have_posts() ) :
    // Now we will use usort to sort the posts array
    @usort( $query->posts, function ( $a, $b ) // @ hides the bug before PHP 7
    {
        // Assign sortable values for categories to sort posts by
        $array = ['a' => $a, 'b' => $b];
        $sort_order_a="";
        $sort_order_b = '';
        foreach ( $array as $key=>$p ) {
            if ( in_category( 'First', $p ) ) {
                ${'sort_order_' . $key} = 1;
            } elseif ( in_category( 'Second', $p ) ) { 
                ${'sort_order_' . $key} = 2;
            } else {
                ${'sort_order_' . $key} = 3;
            }
        }

        // Now that we have a custom sorting order, lets sort the posts
        if ( $sort_order_a !== $sort_order_b ) {
            // Make sure about <, change to > to change sort order
            return $sort_order_a < $sort_order_b; 
        } else { 
            /** 
             * Sort by date if sorting order is the same, 
             * again, to change sort order, change > to <
             */
            return $a->post_date > $b->post_date;
        }
    });

    // Our posts is now sorted, run the loop normally
    // Define our counter to set our columns
    $counter = 0;
    $midpoint = ceil( $query->post_count / 2 );

    while ( $query->have_posts() ) :
        $query->the_post();
            // set our left div on first post
            if ( 0 == $counter ) : 
                ?>
                <div id="left">
                <?php
            endif;

            // Close our left div and open the right one on midpoint
            if ( ( $counter + 1 ) == $midpoint ) :
                <?php
                </div>
                <div id="right">
                <?php
            endif;

            // Lets output the posts with different styling
            if ( in_category( 'First', get_the_ID() ) {
                // Apply styling for category First and display title
                    the_title();
            } elseif ( in_category( 'First', get_the_ID() ) {
                // Apply styling for category Second and display title
                    the_title();
            } else {
                // Apply styling for other posts and display title
                    the_title();
            }

            /**
             * Lets close the div and bail if we have only one post, 
             * or if we are on the last post
             */
            if (    1 == $post_count 
                 || ( $counter + 1 ) == $post_count
            ) :
                ?> 
                </div>
                <?php
            endif;

            // Update our counter
            $counter++;

    endwhile;
    wp_reset_postdata();
endif;

ORIGINAL ANSWER

Your second block of code won’t work and beats the whole reason for your previous question. Just a note on that code, you should not be using $wp_query as a local variable, it is is a reserved global variable which is intenally used in WordPress to hold the main query object. Breaking this global will break a lot of other stuff. What you are doing is what query_posts() does, that is also why you should never use query posts.

Your issue is that your counter is wrongly updated, thus it wrongly adds your divs. What we need to do is to only update our counder in our conditional statements to prevent posts being double counted and therefor stuufing out layout.

We will only use the first loop and modify that accordingly. The only tricky part will be when to open and close divs

EDIT

Due to the code getting a bit techincal, I scrapped my original idea. We will still use the code in block one, except, it would be much easier to loop through the posts as before and create a new array and finally loop through that array and display the posts.

<?php 
$args = array(
    'tax_query' => array(
        array(
            'taxonomy' => 'post-status',
            'field' => 'slug',
            'terms' => array ('post-status-published')
        )
    )
); 
$query = new WP_Query( $args ); 

if ( $query->have_posts() ) :

    /**
     * Get the amount of posts in the loop, this will be used
     * to calculate when to open and close our divs
     */
    $post_count = $query->post_count;

    /**
     * Get a midpoint to break the div into left and right
     */
    $midpoint = ceil( $post_count / 2 );

    $duplicates  = []; 
    $post_titles = [];

    while ( $query->have_posts() ) : 
        $query->the_post(); 

        if ( in_category( 'First' ) ) :
            $post_titles[] = apply_filters( 'the_title', get_the_title() );
            $duplicates[] = get_the_ID(); 

        endif; 
    endwhile; 

    $query->rewind_posts();

    while ( $query->have_posts() ) : 
        $query->the_post(); 

        if ( in_category( 'Second' ) ) : 
            if ( in_array( get_the_ID(), $duplicates ) ) 
                continue;

            $post_titles[] = apply_filters( 'the_title', get_the_title() );
            $duplicates[] = get_the_ID();

        endif; 
    endwhile;

    $query->rewind_posts();          

    while ( $query->have_posts() ) : 
        $query->the_post();

        if ( in_array( get_the_ID(), $duplicates ) ) 
            continue; 

        $post_titles[] = apply_filters( 'the_title', get_the_title() );

    endwhile; 
    wp_reset_postdata(); 

    // Now that we have an array of post titles sorted, lets display them
    foreach ( $post_titles as $key=>$post_title ) :
        // Open our left div
        if ( 0 == $key ) : 
            ?>
            <div id="left">
            <?php
        endif;

        // Close our left div and open the right one on midpoint
        if ( ( $key + 1 ) == $midpoint ) :
            <?php
            </div>
            <div id="right">
            <?php
        endif;

        // Display the post title
        echo $post_title . '</br>';

        /**
         * Lets close the div and bail if we have only one post, 
         * or if we are on the last post
         */
        if (    1 == $post_count 
             || ( $key + 1 ) == $post_count
        ) :
            ?> 
            </div>
            <?php
            break;
        endif;

    endforeach;

endif; 
?>

IMPORTANT NOTE:

Al code is untested and can be improved as you see fit. Due to being untested, there might be slight syntax errors or small bugs

Leave a Comment