$query->query_var['post_type'] not set for pages

All we need is an easy explanation of the problem, so here it is.

Bulding off of this response from @kaiser about being able to filter by post types on a search page, I wanted to be able to automatically add-in all of the public post types available. So, after some help with @kaiser, I came up with:

function sw_custom_post_type_includes($query) {
    $args = array(
        'public'   => true
    ); 
    $output = 'names'; // names or objects, note names is the default
    $operator = 'and'; // 'and' or 'or'
    $post_types = get_post_types( $args, $output, $operator ); 
    if ( !is_search() && !in_array( get_post_type(), $post_types ) )
        return;

    $query->set( 'post_type', $post_types );
    return $query;
}
add_filter('pre_get_posts', 'sw_custom_post_type_includes');

Which initially seemed to work great, but I ran into an issue -> everything filtered by post type correctly EXCEPT when I tried to use &post_type=page. I was actually getting an undefined index error for post_type. I couldn’t figure out why this wasn’t working. So, I decided to use print_r($query->query_vars) to see if everything was being pulled in correctly. Low and behold – for all post types EXCEPT for pages, that variable was set.

I conferred with another dev on this and they verified the issue. This is what I ended up using as a workaround:

function sw_custom_post_type_includes($query) {
    if(isset($_GET['post_type']) && ((!isset($query->query_vars['post_type'])) || (isset($query->query_vars['post_type']) && $query->query_vars['post_type'] != 'nav_menu_item'))) {
        $query->set('post_type', urldecode($_GET['post_type']));
    }
}
add_action('pre_get_posts', 'sw_custom_post_type_includes');

This honestly seems like an odd workaround for something that has been baffling me for a few hours. Anyone with input on this will be rewarded with virtual high-fives.

How to solve :

I know you bored from this bug, So we are here to help you! Take a deep breath and look at the explanation of your problem. We have many solutions to this problem, But we recommend you to use the first method because it is tested & true method that will 100% work for you.

Method 1

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.

Method 2

As I answered the question, where this one is a follow up and after commenting a lot on the questions here, I’m pretty sure, that you somewhere got another filter or conditional that is holding you back from retrieving a result for pages. 99% chance that this is your problem. You better start with a blank vanilla installation and try to run nothing than this filter to prove, that it’s working. Then walk in and start removing/commenting out all that custom stuff, deactivate plugins, etc. until you know what’s going on.

Method 3

you wrote

if ( !is_search() && !in_array( get_post_type(), $post_types ) )
    return;

if page is not in your $post_types array there’s an empty return;

Actually I don’t see why you add this security, this function is called only when there’s an actual need thanks to the pre_get_posts filter

You should just change the return:

function sw_custom_post_type_includes($query) {
    $args = array(
        'public'   => true
    ); 
    $output = 'names'; // names or objects, note names is the default
    $operator = 'and'; // 'and' or 'or'
    $post_types = get_post_types( $args, $output, $operator ); 
    if ( !is_search() && !in_array( get_post_type(), $post_types ) )
        return $query;  //#### keep it unchanged instead of removing everything ####

    $query->set( 'post_type', $post_types );
    return $query;
}
add_filter('pre_get_posts', 'sw_custom_post_type_includes');

It’s just a thought, I didn’t try

Note: Use and implement method 1 because this method fully tested our system.
Thank you 🙂

All methods was sourced from stackoverflow.com or stackexchange.com, is licensed under cc by-sa 2.5, cc by-sa 3.0 and cc by-sa 4.0

Leave a Reply