Group search results by post type, but hide post types with no results

To avoid types with no results you can e.g.

  • one additional time go through the loop and count the occurrences of each type, the number of returned results is not large (only 10 per page)
  • display the type title when you encounter the first post with the given type (move to inside while loop, I know, less readable code)
  • go through the loop one time and collect post titles to the table divided into types

First option

<ul class="search-results-list">
    <?php

    $types = array( 'post', 'page', 'glossary' );
    $occurrences = [];
    while( $search_query->have_posts() )
    {
        $search_query->next_post();
        $type = $search_query->post->post_type;
        if ( !isset($occurrences[$type]) )
            $occurrences[$type] = 1;
        else
            $occurrences[$type] += 1;
    }
    rewind_posts();

    foreach( $types as $type ) : 

        if ( !isset($occurrences[$type]) )
            continue;
        ?>
        <li class="search-results-post-type-item post-type-<?php echo $type ?>">
            //
            // remaining code
            //
        </li>

    <?php endforeach; ?>
</ul>

Second option

$types = array( 'post', 'page', 'glossary' );
foreach( $types as $type ) : 

    $type_header_printed = false;
    ?>
    <?php
    while( $search_query->have_posts() ): 
        $search_query->the_post();
        if( $type == get_post_type() ) :

            // -- post type header -----
            if ( !$type_header_printed ) : 

                $type_header_printed = true;
                ?>
                <li class="search-results-post-type-item post-type-<?php echo $type ?>">
                    <header class="post-type-header">
                        <h5 class="post-type-title">
                            <?php
                                $post_type_obj = get_post_type_object( $type );
                                echo $post_type_obj->labels->name
                            ?>
                        </h5>
                    </header>                    
                    <ul class="search-results-list">

            <?php // -- header end -----
            endif; ?>

            <li class="search-results-list-item">
                <h4 class="entry-title"><?php the_title();?></h4>
            </li>
        <?php

        endif;
    endwhile;
    rewind_posts();

    if ( $type_header_printed ) : ?>
            </ul>
        </li>
    <?php endif; ?>

<?php endforeach; ?>

Third option

<ul class="search-results-list">
    <?php

    $types = array( 'post', 'page', 'glossary' );
    $posts_titles = [];
    while( $search_query->have_posts() )
    {
        $search_query->the_post();
        $type = $search_query->post->post_type;
        if ( !isset($posts_titles[$type]) )
            $posts_titles[$type] = [];

        $posts_titles[$type][] = get_the_title();
    }
    rewind_posts();

    foreach( $types as $type ) : 

        if ( !isset($posts_titles[$type]) )
            continue;
        ?>
        <li class="search-results-post-type-item post-type-<?php echo $type ?>">
            <header class="post-type-header">
                <h5 class="post-type-title">
                    <?php
                        $post_type_obj = get_post_type_object( $type );
                        echo $post_type_obj->labels->name
                    ?>
                </h5>
            </header>
            <ul class="search-results-list">

            <?php foreach( $posts_titles[$type] as $title ) : ?>
                <li class="search-results-list-item">
                    <h4 class="entry-title"><?php echo htmlspecialchars($title); ?></h4>
                </li>
            <?php endforeach; ?>

            </ul>
        </li>

    <?php endforeach; ?>
</ul>

Leave a Comment