1

I was looking for a clean way to group my search results by posttype. I currently have 3 post types: Page, Post and Glossary. After a long search this answer in this thread got me what I needed.

The only problem is that there is no check for when a post type has no search results. If a post type has 0 results, e.g. Glossary, it still shows the post type title (and the container around the post type). I want the post type li.search-results-post-type-item to be hidden in that case.

I am not looking for a css/js hacky display: none; solution. I can't imagine this can't be done with PHP.

Thanks in advance!

Current situation

Posts

  • Post search result 1
  • Post search result 2
  • etc.

Pages

  • Page search result 1
  • Page search result 2
  • etc.

Glossary

(empty)

Desired situation

Posts

  • Post search result 1
  • Post search result 2
  • etc.

Pages

  • Page search result 1
  • Page search result 2
  • etc.

My code so far:

<?php
    $search_query = new WP_Query(
    array(
        'posts_per_page'    => 10,
        's'                 => esc_attr( $_POST['keyword'] ),
        'paged'             => $paged,
        'post_status'       => 'publish'
    )
);

if( $search_query->have_posts() ) : ?>

    <div class="search-suggestions-list-header">
        <?php echo $search_query->found_posts.' results found'; ?>
    </div>

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

    <?php
        $types = array( 'post', 'page', 'glossary' );

        foreach( $types as $type ) : ?>

            <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
                    while( $search_query->have_posts() ): $search_query->the_post();
                        if( $type == get_post_type() ) : ?>
                            <li class="search-results-list-item">
                                <h4 class="entry-title"><?php the_title();?></h4>
                            </li>
                        <?php
                        endif;
                    endwhile;
                    wp_reset_postdata();
                    ?>
                </ul>
            </li>

        <?php endforeach; ?>

    </ul>

<?php else:
    echo '<div class="search-suggestions-no-results">
            <p>' . __('Sorry, no results found', 'text-domain') . '</p>
        </div>';
endif;

3 Answers 3

1

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>
0

I has the same trouble.

My solution:

  1. The $post_type array must be outside the WP_Query.
  2. The WP_Query must be inside the foreach statement.

So... Your code goes like this:

<?php
    // Here, the Types
    $types = array( 'post', 'page', 'glossary' );

    foreach( $types as $type ) :

    // Search Args better in a variable (for order purposes)
    $search_args = array(
            'posts_per_page'    => 10,
            's'                 => esc_attr( $_POST['keyword'] ),
            'paged'             => $paged,
            'post_status'       => 'publish',
            'post_type'         => $type // This line was added
        );

    $search_query = new WP_Query($search_args);

    if( $search_query->have_posts() ) : ?>

    <div class="search-suggestions-list-header">
        <?php echo $search_query->found_posts.' results found'; ?>
    </div>

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

        <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
                while( $search_query->have_posts() ): $search_query->the_post();
                    if( $type == get_post_type() ) : ?>
                        <li class="search-results-list-item">
                            <h4 class="entry-title"><?php the_title();?></h4>
                        </li>
                    <?php
                    endif; // I think you'll need for style propouses
                endwhile;
                ?>
            </ul>
        </li>

    </ul>

<?php else:
    echo '<div class="search-suggestions-no-results">
            <p>' . __('Sorry, no results found', 'text-domain') . '</p>
        </div>';
endif;

wp_reset_postdata(); // Here goes the reset

endforeach; // End Foreach ?>

Hope it helps.

-1

Try to put <?php wp_reset_postdata(); ?> just after endif and check it is working or not and let me know if the problem is still persist.

3
  • If I place it after the endif above, the page won't load (I guess stuck in an infinite loop?). If I place it at the very bottom, it has no effect whatsoever.
    – Justin
    Commented Jan 8, 2019 at 10:03
  • 1
    You need to put this after endif; .
    – user158579
    Commented Jan 9, 2019 at 9:20
  • Thanks, but that's what I tried to say. There are two endif; in my code, but neither options work.
    – Justin
    Commented Jan 9, 2019 at 14:09

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.