Opened 7 years ago
Closed 6 years ago
#41679 closed defect (bug) (invalid)
get_the_terms does not return expected results
Reported by: | rslotb | Owned by: | |
---|---|---|---|
Milestone: | Priority: | normal | |
Severity: | normal | Version: | 4.8.1 |
Component: | Taxonomy | Keywords: | reporter-feedback |
Focuses: | Cc: |
Description
Wordpress fails to report the (custom) categories a post is in.
$terms = get_the_terms( $post->ID, 'project_category' );
returns no results.
$terms = wp_get_object_terms( $post->ID, 'project_category' );
does, which I believe to be correct.
I have traced the problem back to:
class-wp-term-query.php.
The caching code that calls this relies on object_ids (all_with_object_id) to come back, but the object_ids are eaten by:
if ( 'all' === $_fields || 'all_with_object_id' === $_fields) { $terms = array_map( 'get_term', $terms ); }
commenting out the 'all_with_object_id' === $_fields
part yields correct results. But I am not sure that is the correct fix. presumably having a WP_Term object come back in this case had some intention?
Change History (5)
#2
@
7 years ago
Well, yes, I debugged through get_the_terms()
, which, as you say, will get the required value from the cache 1202
. At least in my installation, this is always the case, I never get to 1205
. So the cache must be corrupt. Which it is, in my case.
The cache is initialised by {{function update_object_term_cache($object_ids, $object_type), taxonomy.php, 3084}}.
And the results coming back from this:
$terms = wp_get_object_terms( $ids, $taxonomies, array( 'fields' => 'all_with_object_id', 'orderby' => 'name', 'update_term_meta_cache' => false, ) );
are the problem. As you can see, the query specifies 'all_with_object_id', but this is not honoured by the called code. results do come back, but they do not contain object_ids.
After this:
$object_terms = array(); foreach ( (array) $terms as $term ) { $object_terms[ $term->object_id ][ $term->taxonomy ][] = $term->term_id; }
My result is:
$object_terms = ( [] => Array ( [project_category] => Array ( [0] => 60 [1] => 56 ) [category] => Array ( [0] => 4 ) ) )
And after:
foreach ( $ids as $id ) { foreach ( $taxonomies as $taxonomy ) { if ( ! isset($object_terms[$id][$taxonomy]) ) { if ( !isset($object_terms[$id]) ) $object_terms[$id] = array(); $object_terms[$id][$taxonomy] = array(); } } }
It is:
( [] => Array ( [project_category] => Array ( [0] => 60 [1] => 56 ) [category] => Array ( [0] => 4 ) ) [2144] => Array ( [category] => Array ( ) [post_tag] => Array ( ) [project_category] => Array ( ) ) )
With 2144 being my postid. And this is why the cache is corrupt. That second array, without associated terms, is what is put into the cache for 2144.
It it is not that converting $terms
to WP_Term
objects results in a null result. But the result is missing the object_ids (one in each term). The objects coming back each look like this:
WP_Term Object ( [term_id] => 60 [name] => Coming soon [slug] => coming-soon [term_group] => 0 [term_taxonomy_id] => 60 [taxonomy] => project_category [description] => pre-launch stage [parent] => 0 [count] => 2 [filter] => raw )
Which, I hope is obvious, does not contain an [object_id] => 2144
.
Eliminating the call to: array_map( 'get_term', $terms )
keeps the original query result intact, which makes the calling code work as expected.
The problem probably is broader: WP_Query::get_terms()
currently does not honour the passed in 'all_with_object_id' parameter correctly. If there is a unit test for it, I think it needs looking at.
Regards, and thanks for looking at this,
Remco.
#3
@
7 years ago
@rslotb Thanks a lot for the detailed explanation. I think we are getting closer to the root of the issue.
In the general case, object_id
*is* set on WP_Term
objects returned from wp_get_object_terms()
. There are a number of tests that verify this, at least indirectly. See eg https://core.trac.wordpress.org/browser/tags/4.8.1/tests/phpunit/tests/term/wpGetObjectTerms.php?marks=704#L690. Also, if this were not generally the case, much of WordPress would break :) So something more specific is going on here.
It's not totally obvious how this works, so here's a quick rundown. Hopefully this will help you to go back and debug which part of the chain is failing in your case.
- When querying for terms using
all_with_object_id
, thetr.object_id
field is part of the database query. https://core.trac.wordpress.org/browser/tags/4.8.1/src/wp-includes/class-wp-term-query.php?marks=593-595#L586 The resulting objects thus haveobject_id
set. - These objects are then run through
get_term()
. https://core.trac.wordpress.org/browser/tags/4.8.1/src/wp-includes/class-wp-term-query.php?marks=813-815#L811 (so far this matches what you've reported) - A
stdClass
object (the result of the database query) passed toget_term()
will hit this conditional https://core.trac.wordpress.org/browser/tags/4.8.1/src/wp-includes/taxonomy.php?marks=735-738#L724, which results in the database object being passed toWP_Term::__construct()
- The
WP_Term
object is then set up with each property of the__construct()
payload. https://core.trac.wordpress.org/browser/tags/4.8.1/src/wp-includes/class-wp-term.php?marks=205-207#L196 This includesobject_id
, so that$term->object_id
is set as theobject_id
property on theWP_Term
object.
My guess is that one of the filters in get_term()
has a callback that's stripping the object_id
property. https://core.trac.wordpress.org/browser/tags/4.8.1/src/wp-includes/taxonomy.php?marks=761,775#L752 Check before and after these filters.
If that's not it, maybe you can do some more detailed debugging to see exactly where in steps 1-4 the object_id
param is not being properly passed along.
Hi @rslotb - Thanks for your ticket, and welcome to WordPress Trac!
Can you say more about your debugging? If
get_the_terms()
returns a different value fromwp_get_object_terms()
, it suggests that the cache is corrupt. See https://core.trac.wordpress.org/browser/tags/4.8.1/src/wp-includes/category-template.php?marks=1200#L1186. Ifget_the_terms()
finds an empty cache, it should callwp_get_object_terms()
(see line 1202), in which case it would be impossible for the two values to differ.It's also unclear to me how converting
$terms
toWP_Term
objects would result in a null result (I assume "...returns no results" means it's an empty array?). If$terms
is an array of database query results, then theWP_Term
objects should always be populated. What is the value of$terms
before and after line 814? https://core.trac.wordpress.org/browser/tags/4.8.1/src/wp-includes/class-wp-term-query.php?marks=814#L811