There are times when I have wanted to add a search block to a post or page in WordPress and have it only return results from a specific post type, rather than searching all posts types that are included in search.
After a lot of back and forth I have managed to do this with the following code.
The resolves around adding a custom CSS class to your search block in the following format:
post-type--{post_type}
For example, to restrict the results to just a post type called movie
, you would add the class of:
post-type--movie
You can also add multiple classes for restricting to more than one post type. For example adding these classes you restrict the results to posts and movies.
post-type--movie post-type--post
Here is the code you need, you can add this to your themes functions.php
file or indeed in a plugin.
<?php
/**
* Ensures the search block only searches for posts in the post type specified in the block class.
*
* @param string $block_content The block content.
* @param array $block The block.
* @param array $instance The block instance.
*
* @return string The block content.
*/
function hd_search_block_specific_post_type_only( $block_content, $block, $instance ) {
// if this block has a custom classes added under advanced - do nothing.
if ( empty( $block['attrs']['className'] ) ) {
return $block_content;
}
// create an array to store post types in.
$post_types = [];
// split the class name at the space into an array.
$classes = explode( ' ', $block['attrs']['className'] );
// loop through each class.
foreach ( $classes as $class ) {
// if this class contains our special class.
if ( false === strpos( $class, 'post-type--' ) ) {
continue;
}
// split the class at the double dash.
$class_parts = explode( '--', $class );
// get the last part of the class - the post name.
$post_types[] = $class_parts[1];
}
// if we have no post types.
if ( empty( $post_types ) ) {
return $block_content;
}
// check the post types exist.
$post_types = array_filter( $post_types, 'post_type_exists' );
// if we have no post types.
if ( empty( $post_types ) ) {
return $block_content;
}
// loop through each post type.
foreach ( $post_types as $post_type ) {
// replace the standard input with one that adds the post type.
$block_content = str_replace( '<button aria-label="Search"', '<input type="hidden" name="post_type[]" value="' . esc_attr( $post_type ) . '"><button aria-label="Search"', $block_content );
}
// return the block content.
return $block_content;
}
add_filter( 'render_block_core/search', 'hd_search_block_specific_post_type_only', 10, 3 );
If you are worried about your clients having to add the specific classes to a block, just create a block pattern, give it a sensible name (Search Movies for example) and they can simply add the pattern when they want to add a movie search to a post.
Update!
As it happens, there is an easier way to do this, but it does involve using Javascript.
This method involves creating a block variation of the search block. This is done using the code below, first enqueuing some javascript in the editor.
<?php
function hd_register_search_variation_js() {
wp_enqueue_script(
'hd_search_variation_js',
get_template_directory_uri() . '/js/movie-search.js',
array( 'wp-blocks' )
);
}
add_action( 'enqueue_block_editor_assets', 'hd_register_search_variation_js' );
Then create a javascript file called movie-search.js
and add this to your theme directories js
folder.
In that file place the following:
wp.blocks.registerBlockVariation( 'core/search', {
name: 'hd-search-movies',
title: 'Search Movies',
description: 'Displays a search input to search movies.',
icon: 'search',
attributes: {
namespace: 'hd-search-movies',
query: {
post_type: 'movie',
}
},
});
The important part of that code is the query
part where we set the post type.