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;
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;
Share
Improve this question
asked Jan 8, 2019 at 9:53
JustinJustin
4511 bronze badges
3 Answers
Reset to default 1To 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>
I has the same trouble.
My solution:
- The
$post_type
array must be outside theWP_Query
. - The
WP_Query
must be inside theforeach
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.
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.