Make WordPress Core

Ticket #38280: 38280.5.diff

File 38280.5.diff, 11.7 KB (added by desrosj, 6 years ago)

Updated patch adds another meta key for tracking the object types that were counted. Also removes all previously counted object types each time to prevent abandoned meta keys.

  • src/wp-includes/taxonomy.php

     
    29042904        return true;
    29052905}
    29062906
     2907/**
     2908 * Retrieves the term count for a specific object type.
     2909 *
     2910 * @param int    $term_id Term ID.
     2911 * @param string $taxonomy Taxonomy name.
     2912 * @param string $object_type Object type.
     2913 *
     2914 * @return WP_Error|bool|int WP_Error if invalid taxonomy is passed.
     2915 *                           False if object is not in taxonomy.
     2916 *                           Object term count otherwise.
     2917 */
     2918function wp_get_term_count_for_object_type( $term_id, $taxonomy, $object_type ) {
     2919        if ( ! taxonomy_exists( $taxonomy ) ) {
     2920                return new WP_Error( 'invalid_taxonomy', __( 'Invalid taxonomy.' ) );
     2921        }
     2922
     2923        if ( ! is_object_in_taxonomy( $object_type, $taxonomy ) ) {
     2924                return false;
     2925        }
     2926
     2927        $term = get_term( $term_id, $taxonomy );
     2928
     2929        if ( 0 === $term->count ) {
     2930                return 0;
     2931        }
     2932
     2933        $taxonomy_object = get_taxonomy( $taxonomy );
     2934
     2935        if ( 1 < count( $taxonomy_object->object_type ) ) {
     2936                if ( $term_object_count = get_term_meta( $term_id, '_wp_object_count_' . $object_type, true ) ) {
     2937                        return (int) $term_object_count;
     2938                } else {
     2939                        $counted_object_types = (array) get_term_meta( $term_id, '_wp_counted_object_types', true );
     2940
     2941                        /**
     2942             * If the object type has been counted, and other counts exist in meta, we can
     2943             * assume this term has 0 relationships for this object type.
     2944             */
     2945                        if ( in_array( $object_type, $counted_object_types, true ) ) {
     2946                foreach ( $taxonomy_object->object_type as $type ) {
     2947                    if ( $test_meta = get_term_meta( $term_id, '_wp_object_count_' . $type, true ) ) {
     2948                        return 0;
     2949                    }
     2950                }
     2951            }
     2952
     2953                        // No other meta caches existed. Count and try again.
     2954                        if ( wp_update_term_count_now( array( $term_id ), $taxonomy ) ) {
     2955                                return wp_get_term_count_for_object_type( $term_id, $taxonomy, $object_type );
     2956                        }
     2957                }
     2958        } else {
     2959                return $term->count;
     2960        }
     2961}
     2962
    29072963//
    29082964// Cache
    29092965//
     
    33793435
    33803436        $object_types = (array) $taxonomy->object_type;
    33813437
    3382         foreach ( $object_types as &$object_type )
     3438        foreach ( $object_types as &$object_type ) {
    33833439                list( $object_type ) = explode( ':', $object_type );
     3440        }
    33843441
    33853442        $object_types = array_unique( $object_types );
    33863443
     
    33893446                $check_attachments = true;
    33903447        }
    33913448
    3392         if ( $object_types )
     3449        if ( $object_types ) {
    33933450                $object_types = esc_sql( array_filter( $object_types, 'post_type_exists' ) );
     3451        }
    33943452
    33953453        foreach ( (array) $terms as $term ) {
    33963454                $count = 0;
    33973455
     3456                // Remove previous counts to prevent stale data if an object type is removed from a taxonomy.
     3457        $counted_object_types = (array) get_term_meta( $term, '_wp_counted_object_types', true );
     3458
     3459        foreach ( $counted_object_types as $o_type ) {
     3460            delete_term_meta( $term, '_wp_object_count_' . $o_type );
     3461        }
     3462
     3463        delete_term_meta( $term, '_wp_counted_object_types' );
     3464
     3465                $term_count_meta = array();
     3466
     3467                if ( $object_types ) {
     3468                        foreach ( $object_types as $type ) {
     3469                                $current_count = (int) $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(*) FROM $wpdb->term_relationships, $wpdb->posts WHERE $wpdb->posts.ID = $wpdb->term_relationships.object_id AND post_status = 'publish' AND post_type = %s AND term_taxonomy_id = %d", $type, $term ) );
     3470
     3471                                $count += $current_count;
     3472                                $term_count_meta[ $type ] = $current_count;
     3473                        }
     3474                }
     3475
    33983476                // Attachments can be 'inherit' status, we need to base count off the parent's status if so.
    3399                 if ( $check_attachments )
    3400                         $count += (int) $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(*) FROM $wpdb->term_relationships, $wpdb->posts p1 WHERE p1.ID = $wpdb->term_relationships.object_id AND ( post_status = 'publish' OR ( post_status = 'inherit' AND post_parent > 0 AND ( SELECT post_status FROM $wpdb->posts WHERE ID = p1.post_parent ) = 'publish' ) ) AND post_type = 'attachment' AND term_taxonomy_id = %d", $term ) );
     3477                if ( $check_attachments ) {
     3478                        $attachment_count = (int) $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(*) FROM $wpdb->term_relationships, $wpdb->posts p1 WHERE p1.ID = $wpdb->term_relationships.object_id AND ( post_status = 'publish' OR ( post_status = 'inherit' AND post_parent > 0 AND ( SELECT post_status FROM $wpdb->posts WHERE ID = p1.post_parent ) = 'publish' ) ) AND post_type = 'attachment' AND term_taxonomy_id = %d", $term ) );
     3479
     3480                        $count += $attachment_count;
     3481                        $term_count_meta['attachment'] = $attachment_count;
     3482
     3483                        // Re-add attachment so the meta gets saved below.
     3484                        $object_types[] = 'attachment';
     3485                }
     3486
     3487                // Save individual counts for each object type in term meta.
     3488                if ( 1 < count( $term_count_meta ) ) {
     3489                        foreach ( $object_types as $type ) {
     3490                                if ( ! empty( $term_count_meta[ $type ] ) ) {
     3491                                        update_term_meta( $term, '_wp_object_count_' . $type, (int) $term_count_meta[ $type ] );
     3492                                }
     3493                        }
     3494
     3495                        update_term_meta( $term, '_wp_counted_object_types', $object_types );
     3496                } else {
     3497                        foreach ( $object_types as $type ) {
     3498                                delete_term_meta( $term, '_wp_object_count_' . $type );
     3499                        }
    34013500
    3402                 if ( $object_types )
    3403                         $count += (int) $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(*) FROM $wpdb->term_relationships, $wpdb->posts WHERE $wpdb->posts.ID = $wpdb->term_relationships.object_id AND post_status = 'publish' AND post_type IN ('" . implode("', '", $object_types ) . "') AND term_taxonomy_id = %d", $term ) );
     3501            delete_term_meta( $term, '_wp_counted_object_types' );
     3502        }
    34043503
    34053504                /** This action is documented in wp-includes/taxonomy.php */
    34063505                do_action( 'edit_term_taxonomy', $term, $taxonomy->name );
  • tests/phpunit/tests/term/getTerms.php

     
    228228                ), $terms_id_slug );
    229229        }
    230230
    231         /**
     231        /**
    232232         * @ticket 11823
    233          */
     233         */
    234234        function test_get_terms_include_exclude() {
    235235                global $wpdb;
    236236
     
    22192219                $this->assertNotEquals( 'foo', $found );
    22202220        }
    22212221
     2222        /**
     2223         * @ticket 38280
     2224         */
     2225        public function test_wp_get_term_count_for_object_type_single_object_type() {
     2226                $term_id = self::factory()->term->create( array( 'taxonomy' => 'category' ) );
     2227                $post_id = self::factory()->post->create( array( 'post_type' => 'post' ) );
     2228
     2229                wp_set_object_terms( $post_id, array( $term_id ), 'category' );
     2230
     2231                $this->assertEquals( 1, wp_get_term_count_for_object_type( $term_id, 'category', 'post' ) );
     2232        $this->assertFalse( (bool) get_term_meta( $term_id, '_wp_counted_object_types', true ) );
     2233
     2234                $term_object = get_term( $term_id, 'category' );
     2235                $this->assertEquals( 1, $term_object->count );
     2236
     2237                wp_remove_object_terms( $post_id, array( $term_id ), 'category' );
     2238
     2239                $this->assertEquals( 0, wp_get_term_count_for_object_type( $term_id, 'category', 'post' ) );
     2240        }
     2241
     2242        /**
     2243         * @ticket 38280
     2244         */
     2245        public function test_wp_get_term_count_for_object_type_multiple_object_types() {
     2246                register_post_type( 'wptests_cpt' );
     2247                register_taxonomy_for_object_type( 'category', 'wptests_cpt' );
     2248
     2249                $term_id = self::factory()->term->create( array( 'taxonomy' => 'category' ) );
     2250                $post_id = self::factory()->post->create( array( 'post_type' => 'post' ) );
     2251                $custom_post_id = self::factory()->post->create( array( 'post_type' => 'wptests_cpt' ) );
     2252
     2253                $this->assertEquals( 0, wp_get_term_count_for_object_type( $term_id, 'category', 'post' ) );
     2254                $this->assertEquals( 0,  wp_get_term_count_for_object_type( $term_id, 'category', 'wptests_cpt' ) );
     2255        $this->assertEmpty( get_term_meta( $term_id, '_wp_counted_object_types', true ) );
     2256
     2257                wp_set_object_terms( $post_id, array( $term_id ), 'category' );
     2258
     2259                $this->assertEquals( 1, wp_get_term_count_for_object_type( $term_id, 'category', 'post' ) );
     2260                $this->assertEquals( 0, wp_get_term_count_for_object_type( $term_id, 'category', 'wptests_cpt' ) );
     2261                $this->assertEquals( array( 'post', 'wptests_cpt' ), get_term_meta( $term_id, '_wp_counted_object_types', true ) );
     2262
     2263                wp_set_object_terms( $custom_post_id, array( $term_id ), 'category' );
     2264
     2265                $this->assertEquals( 1, wp_get_term_count_for_object_type( $term_id, 'category', 'post' ) );
     2266                $this->assertEquals( 1, wp_get_term_count_for_object_type( $term_id, 'category', 'wptests_cpt' ) );
     2267        $this->assertEquals( array( 'post', 'wptests_cpt' ), get_term_meta( $term_id, '_wp_counted_object_types', true ) );
     2268
     2269                $term_object = get_term( $term_id, 'category' );
     2270                $this->assertEquals( 2, $term_object->count );
     2271
     2272                wp_remove_object_terms( $custom_post_id, array( $term_id ), 'category' );
     2273
     2274                $this->assertEquals( 1, wp_get_term_count_for_object_type( $term_id, 'category', 'post' ) );
     2275                $this->assertEquals( 0, wp_get_term_count_for_object_type( $term_id, 'category', 'wptests_cpt' ) );
     2276        $this->assertEquals( array( 'post', 'wptests_cpt' ), get_term_meta( $term_id, '_wp_counted_object_types', true ) );
     2277
     2278                wp_remove_object_terms( $post_id, array( $term_id ), 'category' );
     2279
     2280                $this->assertEquals( 0, wp_get_term_count_for_object_type( $term_id, 'category', 'post' ) );
     2281                $this->assertEquals( 0, wp_get_term_count_for_object_type( $term_id, 'category', 'wptests_cpt' ) );
     2282        $this->assertEquals( array( 'post', 'wptests_cpt' ), get_term_meta( $term_id, '_wp_counted_object_types', true ) );
     2283    }
     2284
     2285        /**
     2286         * @ticket 38280
     2287         */
     2288        public function test_wp_get_term_count_for_object_type_multiple_object_types_attachment() {
     2289                register_taxonomy_for_object_type( 'category', 'attachment' );
     2290
     2291                $term_id = self::factory()->term->create( array( 'taxonomy' => 'category' ) );
     2292                $post_id = self::factory()->post->create( array( 'post_type' => 'post' ) );
     2293                $attachment_id = self::factory()->attachment->create_upload_object( DIR_TESTDATA . '/images/canola.jpg', $post_id );
     2294
     2295                $this->assertEmpty( wp_get_term_count_for_object_type( $term_id, 'category', 'post' ) );
     2296                $this->assertEmpty( wp_get_term_count_for_object_type( $term_id, 'category', 'attachment' ) );
     2297        $this->assertEmpty( get_term_meta( $term_id, '_wp_counted_object_types', true ) );
     2298
     2299                wp_set_object_terms( $post_id, array( $term_id ), 'category' );
     2300
     2301                $this->assertEquals( 1, wp_get_term_count_for_object_type( $term_id, 'category', 'post' ) );
     2302                $this->assertEquals( 0, wp_get_term_count_for_object_type( $term_id, 'category', 'attachment' ) );
     2303        $this->assertEquals( array( 'post', 'attachment' ), get_term_meta( $term_id, '_wp_counted_object_types', true ) );
     2304
     2305                wp_set_object_terms( $attachment_id, array( $term_id ), 'category' );
     2306
     2307                $this->assertEquals( 1, wp_get_term_count_for_object_type( $term_id, 'category', 'post' ) );
     2308                $this->assertEquals( 1, wp_get_term_count_for_object_type( $term_id, 'category', 'attachment' ) );
     2309        $this->assertEquals( array( 'post', 'attachment' ), get_term_meta( $term_id, '_wp_counted_object_types', true ) );
     2310
     2311                $term_object = get_term( $term_id, 'category' );
     2312                $this->assertEquals( 2, $term_object->count );
     2313
     2314                wp_remove_object_terms( $attachment_id, array( $term_id ), 'category' );
     2315
     2316                $this->assertEquals( 1, wp_get_term_count_for_object_type( $term_id, 'category', 'post' ) );
     2317                $this->assertEquals( 0, wp_get_term_count_for_object_type( $term_id, 'category', 'attachment' ) );
     2318
     2319                wp_remove_object_terms( $post_id, array( $term_id ), 'category' );
     2320
     2321                $this->assertEquals( 0, wp_get_term_count_for_object_type( $term_id, 'category', 'post' ) );
     2322                $this->assertEquals( 0, wp_get_term_count_for_object_type( $term_id, 'category', 'attachment' ) );
     2323        $this->assertEquals( array( 'post', 'attachment' ), get_term_meta( $term_id, '_wp_counted_object_types', true ) );
     2324        }
     2325
    22222326        public static function maybe_filter_count() {
    22232327                return 'foo';
    22242328        }