To clear up some confusion in previous answers, pre_get_posts isn't a filter so you don't need to return anything.
Your only problem that I see is the if:
if ( !is_search() && !in_array( get_post_type(), $post_types ) )
return;
Basically get_post_type() is going to return false during pre_get_posts because the global $post hasn't been set yet (usually set once you start the loop).
I'm not totally sure WHEN you want all public post types and when you don't. If you just want to set all searches to include all public post types, you really just need to check is_search(). You may also want to make sure the query being modified is the main query and not a custom query that a plugin or theme file is creating. The code should look like this:
function range_search_all_public_post_types( $q ) {
if ( is_search() && is_main_query() )
$q->set( 'post_type', get_post_types( array( 'public' => true ) ) );
}
add_action( 'pre_get_posts', 'range_search_all_public_post_types' );
That's it. That will cause all public post types to be queried for searches.
If you want all public post types on the home page and search, use this:
function range_search_all_public_post_types( $q ) {
if ( ( is_search() || is_home() ) && is_main_query() )
$q->set( 'post_type', get_post_types( array( 'public' => true ) ) );
}
add_action( 'pre_get_posts', 'range_search_all_public_post_types' );
UPDATE:
The problem you are having is unique to post_types that have publicly_queryable set to false. You basically want all public types to work even if they are not publicly_queryable. To do that use this code:
function range_search_all_public_post_types( $q ) {
if ( is_search() && is_main_query() && '' == $q->get( 'post_type' ) && ! empty( $_GET['post_type'] ) && post_type_exists( $_GET['post_type'] ) && get_post_type_object( $_GET['post_type'] )->public )
$q->set( 'post_type', $_GET['post_type'] );
}
add_action( 'pre_get_posts', 'range_search_all_public_post_types' );
Basically, if a post_type is in URL but NOT in QP_Query, it COULD be because it's not publicly_queryable, and if so we fix it. Here is what is checked:
- Is this a search page?
- Is this the main query?
- Is there no post_type specified in the query?
- Is there a post_type specified in the URL?
- Does the URL-specified post_type exist?
- Is the URL-specified post_type public?
If all those are true, it sets the post_type to the one in the URL.