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
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()
forpage_id
andis_page
is only causing thequery_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:
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()
returningtrue
.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: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.I replied in that Trac ticket too, but I think the base problem here is a lot simpler than this.
You’re hooking in demo_foo, demo_bar, demo_baz as “Query variables”, but they’re not; at least, not in the intended sense.
While the ?foo=bar string after the URL is often called the query string, the “query variables” are actually just variables that affect the main query. If you don’t hook in a variable, it doesn’t disappear, it just doesn’t get read by the main WP_Query object.
And in your case, that appears to be exactly what you want. Your variables are theme options, they’re not part of the main Query (which gets posts from the database). You’re not using them to select what posts to display, you’re using them to set theme options and such.
If you don’t hook them in as query_vars, then they won’t affect the main WP_Query object at all. You will have to get them from the main $_GET superglobal instead of using get_query_var, and you will have to perform proper sanitization on them to prevent reflective XSS attacks (using esc_attr or whatever is appropriate), but this should solve your primary problem by eliminating the code causing the problem in the first place, instead of adding in an extra workaround.