WordPress 3.7 introduced ‘date_query’ argument for WP_Query
.
That’s pretty useful and allow very complex date-based queries, however something strange happen when using WP_Query
conditional methods on query using that argument.
Example:
$args = array(
'tax_query'=> array(
'relation' => 'AND',
array(
'taxonomy' => 'category',
'field' => 'slug',
'terms' => 'uncategorized'
),
array(
'taxonomy' => 'post_tag',
'field' => 'slug',
'terms' => array('foo', 'bar')
)
),
'date_query' => array(
array(
'year' => $today["year"],
'month' => $today["mon"],
'day' => $today["mday"],
)
)
);
$query = new WP_Query( $args );
The query should returns posts posted today and having ‘uncategorized’ category or ‘foo’ and ‘bar’ tags. And it does, so far so good.
Now consider the following conditional methods:
$query->is_archive(); // returns true, as guessable
$query->is_category(); // returns true, as guessable
$query->is_tag(); // returns true, as guessable
but
$query->is_date(); // returns false
$query->is_day(); // returns false
$query->is_month(); // returns false
$query->is_year(); // returns false
I thought that the problem can be caused by the fact I mixed ‘tax_query’ and ‘date_query’, but that’s not the problem, proof:
$args = array(
'date_query' => array(
array(
'year' => $today["year"],
'month' => $today["mon"],
'day' => $today["mday"],
)
)
);
$query = new WP_Query( $args );
$query->is_date(); // returns false
$query->is_day(); // returns false
$query->is_month(); // returns false
$query->is_year(); // returns false
$query->is_archive(); // returns false this time
Is this a bug, or I’m missing something? (I’ve searched WP Trac, but found nothing related).
The values retrieved by the
is_date()
,is_day(),
etc. methods are set inside theparse_query
method and they really seem to be intended for the main query, whether that is explicitly stated (intended) or not. The code parses the public query variables to determine those settings.My interpretation is that these values aren’t really meant for secondary queries, and really don’t make much sense for secondary queries either. You’ve just run the query, you know that it is a date/tax/whatever query. That part doesn’t need to be determined for you by parsing the query variables.
To did a bit deeper, I’d say the problem stems from
WP_Query
being a bit schizophrenic. It parsed the request to determine which page ought to load, rather than just being a tool to query the database for posts. It does two jobs, rather than one, which is poor design in my opinion. I think distinct code should parse the request and then pass arguments toWP_Query
. Maybe that is just me.Is it a bug? I’d call it that, at least “weird design”. If
WP_Query
is going to do both jobs, it should at least set the class variables consistently, or split the class into two each with a single purpose. Of course, I don’t get to canonically declare something a “bug” or not 🙂Note: This is just an addition to @s_ha_dum answer (which by the upvotes probably be north of this one).
All the “Conditional Tags”/Query methods that determine state, for e.g.
is_date
is_day
is_hour
is_nanosecond
…are all old, so people tend to forget about their existance. And let’s face it: They never have been very popular. How many hourly archives have you encountered in your digital journeys across the interwebs?
But what is interesting and often needed are date ranges, which can be interpreted as completely unrelated to date ARCHIVES – and that is what the
is_
conditionals are. So I could imagine, that those who wrote thedate_query
parts saw exactly no connection between date or time based archives and the according queries.Imagine the following situation:
Even if a lot of core often seems to be “design by accident”, this one actually isn’t. Take a look at the
tax_query
parts ofWP_Query::get_posts()
:Let me leave you with two questions:
I’ll try to make some order, and focus all my thoughts in a answer instead of commenting the 2 good answers by @s_ha_dum and @kaiser (I upvoted both).
First we need to specify that in WordPress there are 2 types of queries, main and secondaries.
Both are instances of same class,
WP_Query
but first difference is that main query object is instanciated by WordPress and saved in the global$wp_query
variable, all the secondary queries must be instantiated by custom code.But there is another big difference: to get posts,
WP_Query
need to receive some arguments: in main query those arguments are taken from url in secondary queries arguments must explicitly passed to constructor or toget_posts
method.A direct consequence of previous is that, for main query, the query arguments -taken from url- can be only scalar variables: strings, integers and booleans: query arguments whose values are arrays can’t be set via urls, infact, any non scalar variable is strip out from query arguments set via urls.
So we have also 2 types of query arguments, “scalar arguments” and “array arguments”.
WordPress uses scalar arguments to setup template tags, infact WordPress workflow is:
is_*
) in global$wp_query
objectWorth noting that even if using url is not possible set non-scalar arguments, is possible to use
'pre_get_posts'
action hook to make the main query use non-scalar variables liketax_query
,meta_query
,date_query
and so on.But
'pre_get_posts'
runs between #3 ad #4 in workflow described above, so even it make possible to use array-based query arguments, the conditional template properties are already setup, so simply using atax_query
or any other non-scalar query argument insidepre_get_posts
don’t affect the template conditional tags (and so the template loaded) unless one explicitly setis_*
properties:That said, seems logic and legit that non-scalar query arguments like
'date_query'
are not used to set template tags, so the question becomes another: why WordPress parsetax_query
arguments even if they can’t affect templates?Even if this question should be asked to core devs, I have an idea.
Notice: next 2 code snippets are bad, here just for proof of concept: don’t do at home.
In previous code I replaced the global
wp_query
with another query that makes use of a tax query. It can appear strange, but it works as expected, and the template loaded iscategory.php
because WordPress set template tags for'tax_query'
(in facts and$GLOBALS['wp_query']->is_category()
will betrue
).If I do same thing using a
'date_query'
:posts will be retrieved as expected (from March 2014), but template loaded will be not
date.php
and$GLOBALS['wp_query']->is_date()
will befalse
.So my guess – and is only a guess- is that in older code, core devs thought about the possibility to override the global
$wp_query
object with one using non-scalar arguments and so they concerned to make template hierarchy working in that case. But in more modern code (and ‘date_query’ appear only on WP 3.7) core devs don’t worried about that because they probably thought there is no reason to be worried about bad practises…Essentially, for how core works, isn’t strange that
date_query
doesn’t set template conditional properties, but is strange thattax_query
does.Just two additional notes.
Looking at template hierarchy, some conditional template tags has no effect on it, e.g.
is_time
and much others have no effect on template choosing.So, which is the usefulness of conditional template tags that doesn’t affect template choosing?
If we answer: “none”, than we have to admit that they are there just for some historical or misterious reason, otherwise if a conditional tag is useful even if doesn’t affect template choosing, then we have to admit that will be useful if WP would set conditional properties for all the non-scalar argumets and not only for
'tax_query'
.Moreover, sometimes in my code I use to create my own class that extends
WP_Query
(and I’m not the only one). Even if most cases the usage of customWP_Query
classes is for secondary queries, is possible that one want to use it also for the main query (far from impossible because is just matter of overwrite a global variable).Sure is a very bad practice overwrite
$wp_query
after it already queried posts, but if one uses an early hook, like'setup_theme'
, there, global$wp_query
is just an empty object and can be override without any issue.But customizing main
WP_Query
object, one can make possible to use non-scalar arguments for main query and allow to set them before the conditional template properties are set.In that case wouldn’t be useful that
WP_Query
would parse all the non-scalar arguments and properly set conditional template properties accordingly?In my opinion, yes, it would be useful, but untill the core will be strongly based on core
WP_Query
class (and thats is closely forever) it probably makes no sense having that in core.Bonus
If someone, for any reason, want to parse
'date_query'
and set conditional template tags, then following code can be used: