How I Built It: Search Results Block

a white iMac showing a screenshot of the blog posts search results on jacobmartella.com while sitting on a white desk in a white room

WordPress does a lot of great things.

But handling search results based on post types out of the box is definitely not one of them.

If you use the built-in search functionality as is on a website with multiple post types, you’re not going to get nice and neat results. Instead, you’re going to get all of the post types mixed in, which isn’t exactly a great experience.

So when I was planning the version 1.1 update for the Crosswinds Blocks plugin, I wanted to help resolve this issue and make it easy for those using the site editor to be able to display search results differently based on the post type being searched, which would be perfect with the search filters block that I was also building for the release.

Combined, these two blocks would give site editors the ability to create a better search experience for their site visitors all for free.

So here’s how I built the post type search results block.

What the Problem Was

As I mentioned before, the out-of-the-box WordPress search experience leaves a lot to be desired on a lot of different levels. And one of them deals with post types.

While there is a post_type parameter that you can add to the search query URL, there isn’t a way to add that as an option in the default WordPress search form. Instead, you have to add it in via PHP to the search form, which is where the search filters block comes into play.

And when it comes to displaying search results based on a post type being searched, it’s just as hard to customize the query and how the results look. Unlike with archive templates where you can create an archive-{post type slug}.php or archive-{post type slug}.html file, the search template has no such option. There is just the search.php or search.html file.

This functionality is something that’s definitely lacking in WordPress, and I ran into this issue myself over on jacobmartella.com. I have a number of different post types that are public facing and need to be searchable. But they really each need to be displayed differently based on post type.

With a classic theme, it was easy enough to customize the search.php file to change how posts were displayed based on post type. But that’s not really possible with block themes.

So I needed to find a way to make that a reality that was easy to use both for myself and anyone else who had the same need.

The Plan for the Block(s)

The plan for the block was simple enough.

There would need to be two blocks. First, there was a parent block that would contain each of the different specific post type results blocks and would display the correct one based on the search query. Also, that specific post type results block would need to be the only block that could be added as a direct child block to it.

Then there would need to be a post type search results child block that was only available in the parent block. This block would come with a query block already inside of it with the inherit query option set to true to get the query from the search template. And there would need to be an option for the block to choose what post type the block was for or “all” as an option if no post type is set for the search query.

That all seemed simple enough. Now it was time to create the block.

Creating the Parent Search Results Block

The easiest block of the two to create was the parent Post Type Search Results block.

Really, the only thing this block needed to do was house the Single Post Types Search Results block so that adding the functionality to the search template was a lot easier. This block allowed inner blocks, and I used the allowedBlocks feature to make sure that the only block that could be added as a direct inner block was the Single Post Types Search Results block.

Other than that, there’s not a whole lot to this block. But the challenge would come with the child block. 

Creating the Post Type Search Results Blocks

Building the Single Post Types Search Results block is where the real challenge would be.

This block would need to allow a user to select a post type to display results for (or all as a catchall). And it would need to add a query block as a direct inner child to display the results.

The first thing I needed to do was create an array of available post types for a site, which would need to be dynamic. Fortunately, WordPress core data has a solution for this. I was able to use the function below along with useState to get the list of all available post types for a user to choose from for the block.

function getPostTypesList() {
		let options = [];
		options.push( { value: 'all', label: __( 'All Post Types' ) } );
		const postTypes = wp.data.select( 'core' ).getPostTypes( { per_page: -1 } );
		if ( null === postTypes ) {
			return options;
		}
		postTypes.forEach( ( post ) => {
			options.push( { value: post.slug, label: post.name } );
		} );
		return options;
}

That list would then get displayed in a ComboboxControl component to display the dropdown to set the post type.

Next, I was able to use the template functionality for innerBlocks to add the query block as the inner block that shows up when the Single Post Types Search Results block is added and set the inherit query option to true so that it actually displays the results.

Finally, since this was a dynamic block, I added conditionals to the template.php file so that it would check to see if the post_type parameter was set in the search and if so, if the block matched the post type being searched for.

if ( isset( $_GET['post_type'] ) && wp_kses_post( $_GET['post_type'] ) === $attributes['postType'] ) {
	?>
	<div <?php echo wp_kses_data( get_block_wrapper_attributes() ); ?>>
		<?php echo wp_kses( do_blocks( $content ), 'post' ); ?>
	</div>
	<?php
} elseif ( ! isset( $_GET['post_type'] ) && 'all' === $attributes['postType'] ) {
	?>
	<div <?php echo wp_kses_data( get_block_wrapper_attributes() ); ?>>
		<?php echo wp_kses( do_blocks( $content ), 'post' ); ?>
	</div>
	<?php
}

And the end result came out to be a block that truly allows a user to customize how results look like based on the post type that’s being searched for. You can use this block along with the Search Filters block to do a lot of cool things for your search pages, especially if you have multiple public-facing post types on your site.

Problems with echoing do_blocks safely

I did encounter an issue with do_blocks while building out this block.

In order to maintain security while echoing out the do_blocks function, you need to escape it correctly. Naturally, I gravitated to the wp_kses function, as it’s what I had used for the search filters blocks.

But when I tried it out, it definitely did not give me the results I was expecting. There were just too many issues to even try to continue like I had with the other blocks. I needed something new.

Fortunately, I discovered that if you add ‘post’ as a second parameter to the wp_kses function, it gives you the result you want with the blocks showing up as they should without having to fiddle around with arrays and what not.

Hopefully that will save you some time if you’re dealing with inner blocks inside dynamic blocks.

What’s Next for the Block

So where does the block go from here?

Well to be honest, I’m not entirely sure. Right now it feels like it’s in a good place. I definitely need to make sure to update the Crosswinds Framework website to highlight the block and what it can do. Really, if we’re being honest, I need to work on my marketing for the Framework as a whole.

But in terms of features, it feels pretty complete. It’s pretty intuitive to use, and hopefully it’s helpful for those who use it. Of course, there are always small tweaks that can be made, and I’m sure someone is going to find an error with it that’ll need to be fixed.

But I’m really happy with how the block has turned out, and using it over on jacobmartella.com has really helped me improve the search functionality on that website. And I hope it can help you with your website’s search.

Leave a Reply

Your email address will not be published. Required fields are marked *