diff --git a/src/wp-includes/taxonomy-functions.php b/src/wp-includes/taxonomy-functions.php index 53435c0bc6..02e0499e84 100644 --- a/src/wp-includes/taxonomy-functions.php +++ b/src/wp-includes/taxonomy-functions.php @@ -2194,7 +2194,8 @@ function wp_delete_category( $cat_ID ) { * @since 2.3.0 * @since 4.2.0 Added support for 'taxonomy', 'parent', and 'term_taxonomy_id' values of `$orderby`. * Introduced `$parent` argument. - * @since 4.4.0 Introduced `$meta_query` and `$update_term_meta_cache` arguments. + * @since 4.4.0 Introduced `$meta_query` and `$update_term_meta_cache` arguments. When `$fields` is 'all' or + * 'all_with_object_id', an array of `WP_Term` objects will be returned. * * @global wpdb $wpdb WordPress database abstraction object. * @@ -2336,12 +2337,31 @@ function wp_get_object_terms($object_ids, $taxonomies, $args = array()) { $objects = false; if ( 'all' == $fields || 'all_with_object_id' == $fields ) { $_terms = $wpdb->get_results( $query ); + $object_id_index = array(); foreach ( $_terms as $key => $term ) { - $_terms[$key] = sanitize_term( $term, $taxonomy, 'raw' ); + $term = sanitize_term( $term, $taxonomy, 'raw' ); + $_terms[ $key ] = $term; + + if ( isset( $term->object_id ) ) { + $object_id_index[ $key ] = $term->object_id; + } } + + update_term_cache( $_terms ); + $_terms = array_map( 'get_term', $_terms ); + + // Re-add the object_id data, which is lost when fetching terms from cache. + if ( 'all_with_object_id' === $fields ) { + foreach ( $_terms as $key => $_term ) { + if ( isset( $object_id_index[ $key ] ) ) { + $_term->object_id = $object_id_index[ $key ]; + } + } + } + $terms = array_merge( $terms, $_terms ); - update_term_cache( $terms ); $objects = true; + } elseif ( 'ids' == $fields || 'names' == $fields || 'slugs' == $fields ) { $_terms = $wpdb->get_col( $query ); $_field = ( 'ids' == $fields ) ? 'term_id' : 'name'; @@ -3555,11 +3575,13 @@ function update_object_term_cache($object_ids, $object_type) { */ function update_term_cache( $terms, $taxonomy = '' ) { foreach ( (array) $terms as $term ) { - $term_taxonomy = $taxonomy; - if ( empty($term_taxonomy) ) - $term_taxonomy = $term->taxonomy; + // Create a copy in case the array was passed by reference. + $_term = $term; - wp_cache_add( $term->term_id, $term, 'terms' ); + // Object ID should not be cached. + unset( $_term->object_id ); + + wp_cache_add( $term->term_id, $_term, 'terms' ); } } diff --git a/tests/phpunit/tests/term/wpGetObjectTerms.php b/tests/phpunit/tests/term/wpGetObjectTerms.php index 18ef61c062..5ff2a000d2 100644 --- a/tests/phpunit/tests/term/wpGetObjectTerms.php +++ b/tests/phpunit/tests/term/wpGetObjectTerms.php @@ -498,6 +498,131 @@ class Tests_Term_WpGetObjectTerms extends WP_UnitTestCase { $this->assertEqualSets( array( $terms[0], $terms[1] ), wp_list_pluck( $found, 'term_id' ) ); } + /** + * @ticket 14162 + */ + public function test_should_return_wp_term_objects_for_fields_all() { + register_taxonomy( 'wptests_tax', 'post' ); + $p = $this->factory->post->create(); + $t = $this->factory->term->create( array( 'taxonomy' => 'wptests_tax' ) ); + wp_set_object_terms( $p, $t, 'wptests_tax' ); + + $found = wp_get_object_terms( $p, 'wptests_tax', array( + 'fields' => 'all', + ) ); + + $this->assertNotEmpty( $found ); + foreach ( $found as $f ) { + $this->assertInstanceOf( 'WP_Term', $f ); + } + } + + /** + * @ticket 14162 + */ + public function test_should_return_wp_term_objects_for_fields_all_with_object_id() { + register_taxonomy( 'wptests_tax', 'post' ); + $p = $this->factory->post->create(); + $t = $this->factory->term->create( array( 'taxonomy' => 'wptests_tax' ) ); + wp_set_object_terms( $p, $t, 'wptests_tax' ); + + $found = wp_get_object_terms( $p, 'wptests_tax', array( + 'fields' => 'all_with_object_id', + ) ); + + $this->assertNotEmpty( $found ); + foreach ( $found as $f ) { + $this->assertInstanceOf( 'WP_Term', $f ); + } + } + + /** + * @ticket 14162 + */ + public function test_should_prime_cache_for_found_terms() { + global $wpdb; + + register_taxonomy( 'wptests_tax', 'post' ); + $p = $this->factory->post->create(); + $t = $this->factory->term->create( array( 'taxonomy' => 'wptests_tax' ) ); + wp_set_object_terms( $p, $t, 'wptests_tax' ); + + $found = wp_get_object_terms( $p, 'wptests_tax', array( + 'fields' => 'all_with_object_id', + ) ); + + $num_queries = $wpdb->num_queries; + $term = get_term( $t ); + $this->assertSame( $num_queries, $wpdb->num_queries ); + } + + /** + * @ticket 14162 + */ + public function test_object_id_should_not_be_cached_with_term_object() { + register_taxonomy( 'wptests_tax', 'post' ); + $p = $this->factory->post->create(); + $t = $this->factory->term->create( array( 'taxonomy' => 'wptests_tax' ) ); + wp_set_object_terms( $p, $t, 'wptests_tax' ); + + $found = wp_get_object_terms( $p, 'wptests_tax', array( + 'fields' => 'all_with_object_id', + ) ); + + foreach ( $found as $f ) { + $this->assertSame( $p, $f->object_id ); + } + + $term = get_term( $t ); + $this->assertFalse( isset( $term->object_id ) ); + } + + /** + * @ticket 14162 + */ + public function test_term_cache_should_be_primed_for_all_taxonomies() { + global $wpdb; + + register_taxonomy( 'wptests_tax1', 'post' ); + register_taxonomy( 'wptests_tax2', 'post' ); + $p = $this->factory->post->create(); + $t1 = $this->factory->term->create( array( 'taxonomy' => 'wptests_tax1' ) ); + $t2 = $this->factory->term->create( array( 'taxonomy' => 'wptests_tax2' ) ); + wp_set_object_terms( $p, $t1, 'wptests_tax1' ); + wp_set_object_terms( $p, $t2, 'wptests_tax2' ); + + $found = wp_get_object_terms( $p, array( + 'wptests_tax1', + 'wptests_tax2', + ), array( + 'fields' => 'all_with_object_id', + ) ); + + $this->assertEqualSets( array( $t1, $t2 ), wp_list_pluck( $found, 'term_id' ) ); + + $num_queries = $wpdb->num_queries; + $term1 = get_term( $t1 ); + $term2 = get_term( $t2 ); + $this->assertSame( $num_queries, $wpdb->num_queries ); + } + + /** + * @ticket 14162 + */ + public function test_object_id_should_be_set_on_objects_that_share_terms() { + register_taxonomy( 'wptests_tax', 'post' ); + $posts = $this->factory->post->create_many( 2 ); + $t = $this->factory->term->create( array( 'taxonomy' => 'wptests_tax' ) ); + wp_set_object_terms( $posts[0], $t, 'wptests_tax' ); + wp_set_object_terms( $posts[1], $t, 'wptests_tax' ); + + $found = wp_get_object_terms( $posts, 'wptests_tax', array( + 'fields' => 'all_with_object_id', + ) ); + + $this->assertEqualSets( $posts, wp_list_pluck( $found, 'object_id' ) ); + } + public function filter_get_object_terms( $terms ) { $term_ids = wp_list_pluck( $terms, 'term_id' ); // all terms should still be objects