Sort the main query in subcategories/terms?

Always great if someone wants to use better, more correct and more efficient ways to get things done. To achieve what you need is not difficult.

WORKSFLOW:

  • Use the usort() to create your custom sort order

  • Use the the_posts filter to sort and return the sorted array of posts just before the loop is executed

  • Inside the loop, all you need to do is to compare the previous post and current post terms and act on that

THE CODE:

Lets look at the code:

First, get the sorting done using usort() and the_posts filter: (Requires at least PHP 5.4+. If you don’t have at least PHP 5.4, then you are sure to run into security issues. Also note, all the code is untested and might be buggy)

add_filter( 'the_posts', function ( $posts, $q )
{
    if ( $q->is_main_query() // Target the main query only
         && $q->is_tax() // Change to target only specific term or taxonomy
    ) {
        /**
         * We will now sort the $posts array before the loop executes. We will use usort()
         *
         * There is a bug in usort causing the following error:
         * usort(): Array was modified by the user comparison function
         * @see https://bugs.php.net/bug.php?id=50688
         * This bug has yet to be fixed, when, no one knows. The only workaround is to suppress the error reporting
         * by using the @ sign before usort
         */
        @usort( $posts, function ( $a, $b )
        {
            /**
             * Get the respective terms from the posts 
             * We will use the first term's name
             */
            $array_a = get_the_terms( $a->ID, 'TAXONOMY_NAME_HERE' )[0]->name;
            $array_b = get_the_terms( $b->ID, 'TAXONOMY_NAME_HERE' )[0]->name;

            // Sort by term name, if they are the same, sort by post date
            if (  $array_a != $array_b ) {
                return strcasecmp( $array_a, $array_b ); // Sort terms alphabetically, ascending
                // return strcasecmp( $array_b, $array_a ); // Sort terms alphabetically, descending
            } else {
                return $a < $b; // Sort by date if terms are the same. Change < to > if the post date order is incorrect
            }
        }
    }
}, 10, 2 );

This should take care of the sorting. As I have stated, just make sure the date sorting is correct within the terms. If not, just change < to >

Now we can display the term names inside the loop. Adjust and modify as needed

if ( have_posts() ) {
    // Define variable to hold previous post term name
    $term_string = '';
    while ( have_posts() ) {
    the_post();
        global $post;
        // Get the post terms. Use the first term's name
        $term_name = get_the_terms( $post->ID, 'TAXONOMY_NAME_HERE' )[0]->name;
        // Display the taxonomy name if previous and current post term name don't match
        if ( $term_string != $term_name )
            echo '<h2>' . $term_name . '</h2>'; // Add styling and tags to suite your needs

        // Update the $term_string variable
        $term_string = $term_name;

        // REST OF YOUR LOOP

    }
}

Leave a Comment