WordPress.org

Make WordPress Core

Opened 5 years ago

Closed 5 years ago

Last modified 5 years ago

#30846 closed defect (bug) (invalid)

Custom query using tag__in sets query_var tag_id

Reported by: philbirnie Owned by:
Milestone: Priority: normal
Severity: normal Version: 4.1
Component: Taxonomy Keywords:
Focuses: Cc:
PR Number:

Description

When creating a custom query and using the tag__in parameter, WP_Query::get_posts() sets tag_id to the first id in the tag__in array. This renders the tag__in array useless. For example, if the first tag id in the tag__in array has no posts; an empty array is returned.

To recreate:

  1. Create 3 or more tags (in my example, I'll refer to them as "A" with ID 2, "B", with ID 3 and "C" with ID 4.
  2. Assign posts to "A" and "B", but do NOT assign any posts to "C". (Tag "C" should have no posts associated with it)
  3. Create a custom query using the tag__in parameter, like so:
		$queryArgs = array(
			'tag__in' => array(4,3,2)
		);

		$query = new WP_Query($queryArgs);

                var_dump($query->query_vars);

                var_dump($query->get_posts());

                
  1. When looping through the query, you'll note that there will be no posts.
  2. Similarly $query->get_posts() returns an empty array.
  3. $query->query_vars will show that tag__in is set correctly, but the tag_id parameter is also set to the first element in the tag__in array.

var_dump($query->query_vars) output from the code above

(Irrelevant parts of array removed, denoted by ellipses)

array (size=61)
  'tag__in' => 
    array (size=3)
      0 => int 4
      1 => int 3
      2 => int 2
  'error' => string '' (length=0)
  'm' => string '' (length=0)
  ...
  'tag' => string '' (length=0)
  'cat' => string '' (length=0)
  'tag_id' => int 4
  ...
  'category__in' => 
    array (size=0)
      empty
  'category__not_in' => 
    array (size=0)
      empty
  'category__and' => 
    array (size=0)
      empty
  'post__in' => 
    array (size=0)
      empty
  'post__not_in' => 
    array (size=0)
      empty
  'tag__not_in' => 
    array (size=0)
      empty
  'tag__and' => 
    array (size=0)
      empty
  'tag_slug__in' => 
    array (size=0)
      empty
  'tag_slug__and' => 
    array (size=0)
      empty
  'post_parent__in' => 
    array (size=0)
      empty
   ...

Attachments (2)

30846.test.diff (899 bytes) - added by boonebgorges 5 years ago.
demo-page.php (1.0 KB) - added by philbirnie 5 years ago.
Demo page template containing query with tagin.

Download all attachments as: .zip

Change History (8)

#1 @DrewAPicture
5 years ago

  • Component changed from General to Taxonomy

#2 @boonebgorges
5 years ago

  • Keywords reporter-feedback added

philbirnie - Thanks for the detailed report.

You're correct that tag_id gets set to the first member of tag__in. This happens primarily for backward compatibility, to ensure that the tag_id query var is always backfilled when viewing a taxonomy archive, in case themes attempt to reference it directly when (eg) setting up page titles. However, it's not referenced when building the SQL query itself. WP_Query converts all the query_vars passed directly to the class into a single unified tax_query; see https://core.trac.wordpress.org/browser/trunk/src/wp-includes/query.php#L1951. This tax query correctly recognizes the tag__in param. The tag_id backfilling happens afterward: https://core.trac.wordpress.org/browser/trunk/src/wp-includes/query.php#L2709

The attached unit test 30846.test.diff shows that this is working properly. Can you look at the logic there to make sure I'm understanding your report correctly?

Double check to make sure you don't have any plugins or other customizations doing anything odd here. If you continue to have the problem even without any plugins, please share more detail about exactly *where* you're generating the query - the only possibility I can see is that there's something funny going on where you're trying to do a taxonomy-related query while on a single term's taxonomy archive page, or something like that.

#3 @philbirnie
5 years ago

boonebgorges - Thank you very much for the reply and unit test. It is possible that I'm doing something out of context and that could be causing the issue. If so, I'm sorry in advance for wasting everyone's time!

Setup

  • This is a completely clean install with no additional plugins. I am using theme Twenty Fifteen.
  • I placed the custom query outlined above on a custom page template; again, so I could isolate the problem. I have attached that file for reference. I created a new page that uses this template and loaded the page.

Unit Test

Thank you for the unit test - it looks like the first assertion in the unit test should cover the issue that I'm seeing because the first tag does not contain any posts. I will install the test suite and verify that the test passes.

Query Issue

It does indeed appear that the term_id (by way of term_taxonomy_id) is getting added to the query, assuming that $query->request is the actual request that fetches the posts. Below is an echo of $query->request. You'll notice that wp_term_relationships.term_taxonomy_id IN (5) is added - and because there are no posts associated with term_taxonomy_id 5, this results in an empty set.

If I remove this from the query (and the subsequent AND of course), I get the results I would expect -- all posts associated with 3, 4 and 5.

SELECT SQL_CALC_FOUND_ROWS  wp_posts.ID FROM wp_posts  INNER JOIN wp_term_relationships ON (wp_posts.ID = wp_term_relationships.object_id)  INNER JOIN wp_term_relationships AS tt1 ON (wp_posts.ID = tt1.object_id) WHERE 1=1  AND ( 
  wp_term_relationships.term_taxonomy_id IN (5) 
  AND 
  tt1.term_taxonomy_id IN (3,4,5)
) AND wp_posts.post_type = 'post' AND (wp_posts.post_status = 'publish') GROUP BY wp_posts.ID ORDER BY wp_posts.post_date DESC LIMIT 0, 10

I will report back after completing the testing.

Last edited 5 years ago by philbirnie (previous) (diff)

@philbirnie
5 years ago

Demo page template containing query with tagin.

#4 @philbirnie
5 years ago

  • Resolution set to invalid
  • Status changed from new to closed

Thanks for the unit test - I'll use that in the future. I figured it out.

I was attempting to run $query->get_posts() after instantiating the query. This was what was causing the extra 'AND' in the MySQL query mentioned in my previous remarks.

I assumed that $query->get_posts would not modify the query when run after instantiation, but that doesn't seem to be the case.

Last edited 5 years ago by philbirnie (previous) (diff)

#5 @boonebgorges
5 years ago

philbirnie - Aha, thanks for the update. This is the same problem (and solution) as what I described here: https://core.trac.wordpress.org/ticket/27193#comment:10 In short, when you set up a new WP_Query object, the get_posts() method is automatically called internally during the setup process. Invoking that method again can cause the kinds of query_var stomping that you've described here. We ought to have better documentation of this behavior.

#6 @ocean90
5 years ago

  • Keywords reporter-feedback removed
  • Milestone Awaiting Review deleted
Note: See TracTickets for help on using tickets.