Assuming your query does indeed have multiple post types, you’ll want to pluck them out before continuing (I assume you’re using $wp_query
as your WP_Query object):
<?php
$all_post_types = array_map(function($item) {
return $item->post_type;
}, $wp_query->posts);
// Now we get a unique list of post types:
$unique_post_types = array_unique($all_post_types);
// Use that unique list to drive multiple loops:
foreach($unique_post_types as $grouping) {
// Build our "title" for each group, using esc_html to sanitize
echo sprintf("<strong>%s</strong>", esc_html($grouping));
//Start from the beginning of the list:
rewind_posts();
while(have_posts()) {
the_post();
if($post->post_type != $grouping) { // not a match for our current grouping?
continue;
}
echo "<p>– " . get_the_title() . "</p>";
}
}
?>
First I grab all post types from $wp_query
, cull to a unique list, then use that to run the loop multiple times (once for each post type), wherein we only show posts for that type. This is a rudimentary example, and won’t give you the “friendly” names of your post types or the exact output you likely desire, but should give at least a skeletal framework around which to build your output.