Search results ordered by custom post types are not grouped

For a WordPress website I have a couple of custom post types, named cities and books, and also the regular blog posts. I have customized the search results (only search in the post title) page so that the results are to be ordered by custom post type.

The problem is that the search results within a custom post type are not really grouped.

Example:

When I search for the letters am, I get the following results in the search results page:

BOOKS
-----
Book 1

CITIES
------
City 1

BOOKS
-----
Book 2
Book 3

CITIES
------
City 2
City 3
City 4

BLOGPOSTS
---------
Post 1
Post 2
Post 3
etc.

What I would like:

BOOKS
-----
Book 1
Book 2
Book 3

CITIES
------
City 1
City 2
City 3
City 4

BLOGPOSTS
---------
Post 1
Post 2
Post 3
etc.

This is my loop:

<?php if(have_posts()) : ?>

    <?php   
        $last_type = "";
        $typecount = 0;
        while ( have_posts() ) : the_post();
    ?>

        <?php
            if ($last_type != $post->post_type){
                $typecount = $typecount + 1;
                if ($typecount > 1){
                    echo '</div><!-- close type-container -->'; //close type container
                }

                // save the post type.
                $last_type = $post->post_type;

                //open type container
                switch ($post->post_type) {
                    case 'books':
                        echo "<div class=\"books-container\"><h2>";
                        _e('Books', 'qode');
                        echo "</h2>";
                    break;

                    case 'cities':
                        echo "<div class=\"cities-container\"><h2>";
                        _e('Destinations', 'qode');
                        echo "</h2>";
                    break;

                    case 'post':
                        echo "<div class=\"blog-container\"><h2>";
                        _e('Blogposts', 'qode');
                        echo "</h2>";
                    break;
                }
            }
        ?>

        <?php if('books' == get_post_type()) : ?>

             <?php get_template_part('templates/books_search', 'loop'); ?>

        <?php elseif('cities' == get_post_type()) : ?>

            <?php get_template_part('templates/cities_search', 'loop'); ?>

        <?php elseif('post' == get_post_type()) : ?>

            <?php get_template_part('templates/blog_search', 'loop'); ?>

        <?php endif; ?>                     

    <?php endwhile; // endwhile have_posts ?>

    <?php // Pagination ?>
    <?php if($qode_options_proya['pagination'] != "0") : ?>
        <?php pagination($wp_query->max_num_pages, $blog_page_range, $paged); ?>
    <?php endif; ?>

    <?php else: //If no posts are present ?>
        <div class="entry nothing-found">                        
            <h3><?php _e('Sorry, there is no 100% ', 'qode') . " " . the_search_query() . " " . _e(' yet.') ?></h3>
            <p><?php _e('But there are loads of other 100% destinations available. Try again in the searchfield below.', 'qode'); ?> 
        </div>
<?php endif; // endif have_posts ?>

And this is an example of the template file for a CPT that is being included:

<div <?php post_class('vc_span4 wpb_column column_container'); ?> style="padding-bottom: 60px;">

<div class="wpb_wrapper book">

    <?php // Get the post thumbnail ?>
    <div class="wpb_single_image wpb_content_element element_from_fade element_from_fade_on">
        <div class="wpb_wrapper">
            <?php 
                if ( has_post_thumbnail() ) {
                    echo '<a href="' . get_permalink( $thumbnail->ID ) . '" title="' . the_title_attribute( 'echo=0' ) . '" class="book-holder" ><span class="book-thumb">';
                    echo get_the_post_thumbnail( $post->ID, 'book-thumbnail' ); 
                    echo '</span></a>';
                }
            ?>
        </div> 
    </div>

</div>

<div class="wpb_text_column wpb_content_element text-align-center" style="margin: 20px 0px 0px 0px; ">

    <div class="wpb_wrapper">
        <h5><?php the_title(); ?></h5>
    </div>
    <a href="<?php the_permalink(); ?>" target="_blank" class="qbutton  small" style="margin: 20px 0px 0px 0px; "><?php _e('More Info', 'qode'); ?></a>

</div>
</div>

I must be doing something wrong here, but I just can’t figure out what.

Can someone suggest me where I am going wrong here?

Solutions Collecting From Web of "Search results ordered by custom post types are not grouped"

This type of ordering is not available by default. You however have two basic options here

  • rewind_posts() -> Rerun the loop multiple times and use rewind_posts() to rewind the loop so you can rerun it again

EXAMPLE

if ( have_posts() ) {
    while( have_posts() ) {
        the_post();

        if ( $post->post_type == 'books' ) {
            // Output books posts
        }

    } // end first while loop

    rewind_posts(); // rewind loop so we can rerun it

    while( have_posts() ) { // Start second while loop
        the_post();

        if ( $post->post_type == 'cities' ) {
            // Output cities posts
        }

    } // end second while loop

    rewind_posts(); // rewind loop so we can rerun it

    // Run your third while loop for blog posts

} // End your if statement
  • The second method is to use usort and the the_posts filter to sort your posts accordingle before the loop is run.

EXAMPLE

(This goes into your functions.php)

add_filter( 'the_posts', function( $posts, $q ) 
{
    if(     $q->is_main_query()  // Targets the main query only
         && $q->is_search() // Only targets the search page
    ) {

        usort( $posts, function( $a, $b )
      {
            $post_types = array ( // Set your post type order here
                'books' => 1,
                'cities' => 2,
                'post' => 3
            );

            $posts = $post_types[$a->post_type] - $post_types[$b->post_type];

            if ($posts === 0) {
                //same post_type, compare by post_date.
                return $a->post_date < $b->post_date;
            }else{
                //different post_type, save to ignore the post_date.
                return $posts;
            }
        } );
    } 
    return $posts;  
}, 
10, 2 );