How to force a query conditional?

There is a long-standing WordPress core bug (#16373) in which, if custom query variables are registered and present in the query string, the query will not set is_front_page() true, even if 'page' == get_option( 'show_on_front' ). Full details are in the trac ticket, but the end result is that the front page returns an invalid page instead of get_option( 'page_on_front' ).

I have a certain use case in which I need to pass custom query variables to the query string on the front page (using a form to pass registered query variables, which are in turn used to filter Theme options – a front-end Theme options demo). The above-mentioned trac ticket was closed as invalid, so I need a work-around.

I can force the front page template easily enough, like so:

function themeslug_force_front_page_template( $template ) {
    if ( '' != get_query_var( 'foobar' ) ) { // Registered custom query var
        return get_front_page_template();
    }
    return $template;
}
add_filter( 'template_include', 'themeslug_force_front_page_template' );

However, the underlying query conditionals are not impacted. So any code dependent on is_front_page() being true (such as, say, a slider that only displays on the site front page) will still not render properly.

I have attempted to modify $query at pre_get_posts, but this does not appear to be working:

function themeslug_force_static_front_page( $query ) {
    if ( $query->is_main_query() ) {
        if ( 'page' == get_option( 'show_on_front' ) ) {
            if ( '' != get_query_var( 'foobar' ) ) { // Registered custom query var
                $query->set( 'page_id', get_option( 'page_on_front' ) );
                $query->set( 'is_home', false );
                $query->set( 'is_page', true );
                $query->set( 'is_front_page', true );
            }
        }
    }
}
add_action( 'pre_get_posts', 'themeslug_force_static_front_page' );

The query conditionals are not modified. Am I attempting incorrectly inside my callback? Is there a different/better way to force the query conditionals, in particular is_front_page()?

Edit

Note that setting the page_id does work in the pre_get_posts callback:

$query->set( 'page_id', get_option( 'page_on_front' ) );

If I omit the template_include filter, the displayed page is, indeed, the page assigned to the front page; however, get_page_template() is used, rather than get_front_page_template(). So, setting page_id at pre_get_posts does cause is_page() to be true. But the aforementioned bug prevents is_front_page() from being set to true.

Edit 2

Per @toscho’s request, here’s some query var code, for context.

Register query variables:

/**
 * Add options-related query variables
 */
function themslug_add_theme_demo_query_vars( $qvars ) {
    $qvars[] = 'demo_foo';
    $qvars[] = 'demo_bar';
    $qvars[] = 'demo_baz';
    return $qvars;
}
add_filter( 'query_vars', 'themeslug_add_theme_demo_query_vars' );

(Where foo, bar, and baz are Theme options – background, various colors, etc.)

How they are used is simply to add a filter to a Theme option – that part is out of the scope of the question, so I’ll omit it here.

They are output on the front end via custom Widget. The Widget output is just a form. Here’s an example of one of the form fields:

<h3>Foo</h3>
<input name="demo_foo" id="demo_foo" class="pickcolor"  type="text" value="<?php echo $foo_setting; ?>" data-default-color="<?php echo $foo_setting; ?>" />

Generated query string on-submit:

www.example.com/?foo=some_value

2 s
2

Did you check what part of is_front_page() is causing it to return false?

I could reproduce the problem by following the setup from the trac ticket. In my case inside this function the call to is_page() was returning false.

I guess this is due to using $wp_query->set() for page_id and is_page is only causing the query_vars to be changed, you don’t change the “state of the query” itself.

I was able to create a workaround for this by appending this two lines to your code:

$query->is_page = true;
$query->queried_object = get_post(get_option('page_on_front') );

Which resulted (in my setup) in the page being correctly displayed on the frontpage (despite the query_var being present in the URL) and also is_front_page() returning true.

I hope this is helping you somewhat further.


If you look at the definition of the WP_Query class, you’ll see that there is a variable $query_vars and a variable $is_page.

The set function you used ($query->set( 'is_page', true );) does only set a query var:

function set($query_var, $value) {
    $this->query_vars[$query_var] = $value;
}

It does not change the state information that is saved in $query->is_page = true, instead it sets the query var $query->query_vars['is_page'] = true.

That is a bit misleading if you come from an OOP-approach and understand WP_Query::set() as a classic class-setter function – which it isn’t.

Leave a Comment