I wanted to exclude pages
from the search results, and found many ways to do it, and was wondering why use !is_admin()
or is_main_query()
and which way would be better.
add_filter('pre_get_posts','search_filter');
function search_filter($query) {
if ($query->is_search) {
$query->set('post_type', 'post');
}
return $query;
}
add_filter('pre_get_posts','search_filter');
function search_filter($query) {
if ( !is_admin() && $query->is_search) {
$query->set('post_type', 'post');
}
return $query;
}
add_action('pre_get_posts','search_filter');
function search_filter($query) {
if ( !is_admin() && $query->is_main_query() ) {
if ($query->is_search) {
$query->set('post_type', 'post');
}
}
}
2 Answers
Note that when we use:
$query->set( 'post_type', 'post' );
then we’re overriding all searchable post types, not only the page
post type.
That may be just fine in some cases, and we’re done using some of your pre_get_posts
snippets that fit our needs.
But sometimes we don’t want to hard fix it that way. Here we discuss that kind of scenarios.
Using the register_post_type_args filter.
When the post type isn’t specified, the WP_Query
search uses any post types that are searchable, namely:
$in_search_post_types = get_post_types( array('exclude_from_search' => false) );
When we register a post type, we can set the exclude_from_search
parameter as false to exclude it from search.
We can modify it for the page
post type setup with:
add_filter( 'register_post_type_args', function( $args, $name )
{
// Target 'page' post type
if( 'page' === $name )
$args['exclude_from_search'] = true;
return $args;
}, 10, 2 );
More about register_post_type()
here.
Examples
Here are examples where the page post type would be excluded from the search, using the above filtering:
-
Main query search on the front-end with
https://example.tld?s=testing
-
Secondary query like:
$query = new WP_Query( [ 's' => 'testing' ] );
-
Secondary query like:
$query = new WP_Query( [ 's' => 'testing', 'post_type' => 'any' ] );
Some notes on queries with pre set post types:
Let’s consider cases where the post types are fixed, like:
$query = new WP_Query( [ 's' => 'testing', 'post_type' => [ 'page', 'post'] ] );
If the post type is set by some array $post_type
, then we can filter the 'page'
out it with
if( is_array( $post_type ) && count( $post_type ) > 1 )
{
$post_type = array_filter(
$post_type,
function( $item ) { return 'page' !== $item; }
);
}
If we don’t have direct access to that array, we could use e.g. pre_get_posts
to remove the ‘page’ from the post type array, with help of the get
/set
methods of WP_Query
. Here’s an example for the main search query on the front end:
add_action( 'pre_get_posts', function search_filter( \WP_Query $query )
{
if( ! $query->is_search() || ! $query->is_main_query() || ! is_admin() )
return;
$post_type = $query->get( 'post_type' );
if( is_array( $post_type ) && count( $post_type ) > 1 )
{
$post_type = array_filter(
$post_type,
function( $item ) { return 'page' !== $item; }
);
$query->set('post_type', $post_type );
}
} );
Why did we check the array count > 1 here?
That’s because we should be careful to removing 'page'
from examples like:
$query = new WP_Query( [ 's' => 'testing', 'post_type' => [ 'page' ] ] );
$query = new WP_Query( [ 's' => 'testing', 'post_type' => 'page' ] );
as an empty array or an empty string, for the post type:
$query = new WP_Query( [ 's' => 'testing', 'post_type' => [] ] );
$query = new WP_Query( [ 's' => 'testing', 'post_type' => '' ] );
will fall back to the 'post'
post type.
Note that:
$query = new WP_Query( [ 's' => 'testing', 'post_type' => 'page, post' ] );
isn’t supported, as the resulting post type would be 'pagepost'
.
In these cases, where we don’t have direct access to the WP_Query
objects, we could halt the query with tricks like 'post__in' => []
or 1=0
in the search WHERE query part or even play with the posts_pre_query
filter or using some more advanced methods. There are plenty of answers on this site about that. This and this is what I recall at the moment.
The null
case:
$query = new WP_Query( [ 's' => 'testing', 'post_type' => null ] );
falls back to 'any'
post types:
Hope it helps!
PS:
Also note the inconsistency in your snippets, as you have both
add_filter('pre_get_posts','search_filter');
and
add_action('pre_get_posts','search_filter');
It’s considered an action, but it will not make any difference, as actions are wrapped as filters, behind the scenes.