Ticket #40351: 40351.2.diff
File 40351.2.diff, 34.7 KB (added by , 3 months ago) |
---|
-
src/wp-includes/class-wp-taxonomy.php
diff --git a/src/wp-includes/class-wp-taxonomy.php b/src/wp-includes/class-wp-taxonomy.php index c34a03c38e..21f3d4aec9 100644
a b final class WP_Taxonomy { 180 180 */ 181 181 public $update_count_callback; 182 182 183 /** 184 * Function that will be called when the count is modified by an amount. 185 * 186 * @since 5.6.0 187 * @var callable 188 */ 189 public $update_count_by_callback; 190 183 191 /** 184 192 * Whether this taxonomy should appear in the REST API. 185 193 * … … public function set_props( $object_type, $args ) { 277 285 $args = apply_filters( 'register_taxonomy_args', $args, $this->name, (array) $object_type ); 278 286 279 287 $defaults = array( 280 'labels' => array(), 281 'description' => '', 282 'public' => true, 283 'publicly_queryable' => null, 284 'hierarchical' => false, 285 'show_ui' => null, 286 'show_in_menu' => null, 287 'show_in_nav_menus' => null, 288 'show_tagcloud' => null, 289 'show_in_quick_edit' => null, 290 'show_admin_column' => false, 291 'meta_box_cb' => null, 292 'meta_box_sanitize_cb' => null, 293 'capabilities' => array(), 294 'rewrite' => true, 295 'query_var' => $this->name, 296 'update_count_callback' => '', 297 'show_in_rest' => false, 298 'rest_base' => false, 299 'rest_controller_class' => false, 300 'default_term' => null, 301 '_builtin' => false, 288 'labels' => array(), 289 'description' => '', 290 'public' => true, 291 'publicly_queryable' => null, 292 'hierarchical' => false, 293 'show_ui' => null, 294 'show_in_menu' => null, 295 'show_in_nav_menus' => null, 296 'show_tagcloud' => null, 297 'show_in_quick_edit' => null, 298 'show_admin_column' => false, 299 'meta_box_cb' => null, 300 'meta_box_sanitize_cb' => null, 301 'capabilities' => array(), 302 'rewrite' => true, 303 'query_var' => $this->name, 304 'update_count_callback' => '', 305 'update_count_by_callback' => '', 306 'show_in_rest' => false, 307 'rest_base' => false, 308 'rest_controller_class' => false, 309 'default_term' => null, 310 '_builtin' => false, 302 311 ); 303 312 304 313 $args = array_merge( $defaults, $args ); … … public function set_props( $object_type, $args ) { 411 420 ); 412 421 } 413 422 423 // If generic update callback is defined but increment/decrement callback is not. 424 if ( 425 ! empty( $args['update_count_callback'] ) && 426 is_callable( $args['update_count_callback'] ) && 427 empty( $args['update_count_by_callback'] ) 428 ) { 429 $args['update_count_by_callback'] = function( $tt_ids, $taxonomy, $modify_by ) { 430 return call_user_func( $args['update_count_callback'], $tt_ids, $taxonomy ); 431 }; 432 } 433 414 434 foreach ( $args as $property_name => $property_value ) { 415 435 $this->$property_name = $property_value; 416 436 } -
src/wp-includes/post.php
diff --git a/src/wp-includes/post.php b/src/wp-includes/post.php index 9456bec763..988c7ae79f 100644
a b function wp_insert_post( $postarr, $wp_error = false ) { 4035 4035 clean_post_cache( $post_ID ); 4036 4036 } 4037 4037 4038 // Allow term counts to be handled by transitioning post type. 4039 _wp_prevent_term_counting( true ); 4038 4040 if ( is_object_in_taxonomy( $post_type, 'category' ) ) { 4039 4041 wp_set_post_categories( $post_ID, $post_category ); 4040 4042 } … … function wp_insert_post( $postarr, $wp_error = false ) { 4091 4093 } 4092 4094 } 4093 4095 } 4096 // Restore term counting. 4097 _wp_prevent_term_counting( false ); 4094 4098 4095 4099 if ( ! empty( $postarr['meta_input'] ) ) { 4096 4100 foreach ( $postarr['meta_input'] as $field => $value ) { … … function wp_publish_post( $post ) { 4399 4403 if ( ! $default_term_id ) { 4400 4404 continue; 4401 4405 } 4406 _wp_prevent_term_counting( true ); 4402 4407 wp_set_post_terms( $post->ID, array( $default_term_id ), $taxonomy ); 4408 _wp_prevent_term_counting( false ); 4403 4409 } 4404 4410 4405 4411 $wpdb->update( $wpdb->posts, array( 'post_status' => 'publish' ), array( 'ID' => $post->ID ) ); … … function wp_queue_posts_for_term_meta_lazyload( $posts ) { 7265 7271 * @param WP_Post $post Post object. 7266 7272 */ 7267 7273 function _update_term_count_on_transition_post_status( $new_status, $old_status, $post ) { 7268 // Update counts for the post's terms. 7274 if ( 'inherit' === $new_status ) { 7275 $new_status = get_post_status( $post->post_parent ); 7276 } 7277 7278 if ( 'inherit' === $old_status ) { 7279 $old_status = get_post_status( $post->post_parent ); 7280 } 7281 7282 $count_new = 'publish' === $new_status; 7283 $count_old = 'publish' === $old_status; 7284 7285 if ( $count_new === $count_old ) { 7286 // Nothing to do. 7287 return; 7288 } 7289 7290 /* 7291 * Update counts for the post's terms. 7292 * 7293 * Term counting is deferred while incrementing/decrementing the counts to 7294 * reduce the number of database queries required. Once the counts are 7295 * complete the updates are performed if term counting wasn't previously 7296 * deferred. 7297 */ 7298 $previous_deferred_setting = wp_defer_term_counting(); 7299 wp_defer_term_counting( true ); 7269 7300 foreach ( (array) get_object_taxonomies( $post->post_type ) as $taxonomy ) { 7270 7301 $tt_ids = wp_get_object_terms( $post->ID, $taxonomy, array( 'fields' => 'tt_ids' ) ); 7271 wp_update_term_count( $tt_ids, $taxonomy ); 7302 7303 if ( empty( $tt_ids ) ) { 7304 // No terms for this taxonomy on object. 7305 continue; 7306 } 7307 7308 $object_types = (array) get_taxonomy( $taxonomy )->object_type; 7309 7310 foreach ( $object_types as &$object_type ) { 7311 list( $object_type ) = explode( ':', $object_type ); 7312 } 7313 7314 $object_types = array_unique( $object_types ); 7315 7316 if ( ! in_array( $post->post_type, $object_types, true ) ) { 7317 $modify_by = 0; 7318 } elseif ( $count_new && ! $count_old ) { 7319 $modify_by = 1; 7320 } elseif ( $count_old && ! $count_new ) { 7321 $modify_by = -1; 7322 } 7323 7324 if ( 'attachment' === $post->post_type ) { 7325 wp_modify_term_count_by( $tt_ids, $taxonomy, $modify_by ); 7326 continue; 7327 } 7328 7329 $check_attachments = array_search( 'attachment', $object_types, true ); 7330 if ( false !== $check_attachments ) { 7331 unset( $object_types[ $check_attachments ] ); 7332 $check_attachments = true; 7333 } 7334 7335 wp_modify_term_count_by( $tt_ids, $taxonomy, $modify_by ); 7336 if ( ! $check_attachments ) { 7337 continue; 7338 } 7339 7340 /* 7341 * For non-attachments, check if there are any attachment children 7342 * with 'inherited' post status -- if so those will need to be counted. 7343 */ 7344 $attachments = get_children( 7345 array( 7346 'post_parent' => $post->ID, 7347 'post_status' => 'inherit', 7348 'post_type' => 'attachment', 7349 'update_post_meta_cache' => false, 7350 'update_post_term_cache' => true, 7351 ) 7352 ); 7353 7354 foreach ( $attachments as $attachment ) { 7355 _update_term_count_on_transition_post_status( $new_status, $old_status, $attachment ); 7356 } 7272 7357 } 7358 wp_defer_term_counting( $previous_deferred_setting ); 7273 7359 } 7274 7360 7275 7361 /** -
src/wp-includes/taxonomy.php
diff --git a/src/wp-includes/taxonomy.php b/src/wp-includes/taxonomy.php index cc491a87dd..cfdb09b626 100644
a b function wp_set_object_terms( $object_id, $terms, $taxonomy, $append = false ) { 2561 2561 return new WP_Error( 'invalid_taxonomy', __( 'Invalid taxonomy.' ) ); 2562 2562 } 2563 2563 2564 $taxonomy_object = get_taxonomy( $taxonomy ); 2565 2566 $object_types = (array) $taxonomy_object->object_type; 2567 foreach ( $object_types as &$object_type ) { 2568 if ( 0 === strpos( $object_type, 'attachment:' ) ) { 2569 list( $object_type ) = explode( ':', $object_type ); 2570 } 2571 } 2572 2573 if ( array_filter( $object_types, 'post_type_exists' ) !== $object_types ) { 2574 // This taxonomy applies to non-posts, count changes now. 2575 $do_recount = ! _wp_prevent_term_counting(); 2576 } elseif ( 'publish' === get_post_status( $object_id ) ) { 2577 // Published post, count changes now. 2578 $do_recount = ! _wp_prevent_term_counting(); 2579 } else { 2580 $do_recount = false; 2581 } 2582 2564 2583 if ( ! is_array( $terms ) ) { 2565 2584 $terms = array( $terms ); 2566 2585 } … … function wp_set_object_terms( $object_id, $terms, $taxonomy, $append = false ) { 2646 2665 $new_tt_ids[] = $tt_id; 2647 2666 } 2648 2667 2649 if ( $new_tt_ids ) {2650 wp_ update_term_count( $new_tt_ids, $taxonomy );2668 if ( $new_tt_ids && $do_recount ) { 2669 wp_increment_term_count( $new_tt_ids, $taxonomy ); 2651 2670 } 2652 2671 2653 2672 if ( ! $append ) { … … function wp_set_object_terms( $object_id, $terms, $taxonomy, $append = false ) { 2665 2684 } 2666 2685 } 2667 2686 2668 $t = get_taxonomy( $taxonomy ); 2669 2670 if ( ! $append && isset( $t->sort ) && $t->sort ) { 2687 if ( ! $append && isset( $taxonomy_object->sort ) && $taxonomy_object->sort ) { 2671 2688 $values = array(); 2672 2689 $term_order = 0; 2673 2690 … … function wp_remove_object_terms( $object_id, $terms, $taxonomy ) { 2748 2765 return new WP_Error( 'invalid_taxonomy', __( 'Invalid taxonomy.' ) ); 2749 2766 } 2750 2767 2768 $taxonomy_object = get_taxonomy( $taxonomy ); 2769 2770 $object_types = (array) $taxonomy_object->object_type; 2771 foreach ( $object_types as &$object_type ) { 2772 if ( 0 === strpos( $object_type, 'attachment:' ) ) { 2773 list( $object_type ) = explode( ':', $object_type ); 2774 } 2775 } 2776 2777 if ( array_filter( $object_types, 'post_type_exists' ) !== $object_types ) { 2778 // This taxonomy applies to non-posts, count changes now. 2779 $do_recount = ! _wp_prevent_term_counting(); 2780 } elseif ( 2781 'publish' === get_post_status( $object_id ) || 2782 ( 2783 'inherit' === get_post_status( $object_id ) && 2784 'publish' === get_post_status( wp_get_post_parent_id( $object_id ) ) 2785 ) 2786 ) { 2787 // Published post, count changes now. 2788 $do_recount = ! _wp_prevent_term_counting(); 2789 } else { 2790 $do_recount = false; 2791 } 2792 2751 2793 if ( ! is_array( $terms ) ) { 2752 2794 $terms = array( $terms ); 2753 2795 } … … function wp_remove_object_terms( $object_id, $terms, $taxonomy ) { 2806 2848 */ 2807 2849 do_action( 'deleted_term_relationships', $object_id, $tt_ids, $taxonomy ); 2808 2850 2809 wp_update_term_count( $tt_ids, $taxonomy ); 2851 if ( $do_recount ) { 2852 wp_decrement_term_count( $tt_ids, $taxonomy ); 2853 } 2810 2854 2811 2855 return (bool) $deleted; 2812 2856 } … … function wp_defer_term_counting( $defer = null ) { 3226 3270 $_defer = $defer; 3227 3271 // Flush any deferred counts. 3228 3272 if ( ! $defer ) { 3273 wp_modify_term_count_by( null, null, null, true ); 3229 3274 wp_update_term_count( null, null, true ); 3230 3275 } 3231 3276 } … … function wp_defer_term_counting( $defer = null ) { 3233 3278 return $_defer; 3234 3279 } 3235 3280 3281 /** 3282 * Prevents add/removing a term from modifying a term count. 3283 * 3284 * This is used by functions calling wp_transition_post_status() to indicate the 3285 * term count will be handled during the post's transition. 3286 * 3287 * @private 3288 * @since 5.6.0 3289 * 3290 * @param bool $new_setting The new setting for preventing term counts. 3291 * @return bool Whether term count prevention is enabled or disabled. 3292 */ 3293 function _wp_prevent_term_counting( $new_setting = null ) { 3294 static $prevent = false; 3295 3296 if ( is_bool( $new_setting ) ) { 3297 $prevent = $new_setting; 3298 } 3299 3300 return $prevent; 3301 } 3302 3303 /** 3304 * Increments the amount of terms in taxonomy. 3305 * 3306 * If there is a taxonomy callback applied, then it will be called for updating 3307 * the count. 3308 * 3309 * The default action is to increment the count by one and update the database. 3310 * 3311 * @since 5.6.0 3312 * 3313 * @param int|array $tt_ids The term_taxonomy_id of the terms. 3314 * @param string $taxonomy The context of the term. 3315 * @param int $increment_by By how many the term count is to be incremented. Default 1. 3316 * @param bool $do_deferred Whether to flush the deferred term counts too. Default false. 3317 * @return bool If no terms will return false, and if successful will return true. 3318 */ 3319 function wp_increment_term_count( $tt_ids, $taxonomy, $increment_by = 1, $do_deferred = false ) { 3320 return wp_modify_term_count_by( $tt_ids, $taxonomy, $increment_by, $do_deferred ); 3321 } 3322 3323 /** 3324 * Decrements the amount of terms in taxonomy. 3325 * 3326 * If there is a taxonomy callback applied, then it will be called for updating 3327 * the count. 3328 * 3329 * The default action is to decrement the count by one and update the database. 3330 * 3331 * @since 5.6.0 3332 * 3333 * @param int|array $tt_ids The term_taxonomy_id of the terms. 3334 * @param string $taxonomy The context of the term. 3335 * @param int $decrement_by By how many the term count is to be decremented. Default 1. 3336 * @param bool $do_deferred Whether to flush the deferred term counts too. Default false. 3337 * @return bool If no terms will return false, and if successful will return true. 3338 */ 3339 function wp_decrement_term_count( $tt_ids, $taxonomy, $decrement_by = 1, $do_deferred = false ) { 3340 return wp_modify_term_count_by( $tt_ids, $taxonomy, $decrement_by * -1, $do_deferred ); 3341 } 3342 3343 /** 3344 * Modifies the amount of terms in taxonomy. 3345 * 3346 * If there is a taxonomy callback applied, then it will be called for updating 3347 * the count. 3348 * 3349 * The default action is to decrement the count by one and update the database. 3350 * 3351 * @since 5.6.0 3352 * 3353 * @param int|array $tt_ids The term_taxonomy_id of the terms. 3354 * @param string $taxonomy The context of the term. 3355 * @param int $modify_by By how many the term count is to be modified. 3356 * @param bool $do_deferred Whether to flush the deferred term counts too. Default false. 3357 * @return bool If no terms will return false, and if successful will return true. 3358 */ 3359 function wp_modify_term_count_by( $tt_ids, $taxonomy, $modify_by, $do_deferred = false ) { 3360 static $_deferred = array(); 3361 3362 if ( $do_deferred ) { 3363 foreach ( (array) $_deferred as $taxonomy_name => $modifications ) { 3364 $tax_by_count = array_reduce( 3365 array_keys( $modifications ), 3366 function( $by_count, $tt_id ) use ( $modifications ) { 3367 if ( ! isset( $by_count[ $modifications[ $tt_id ] ] ) ) { 3368 $by_count[ $modifications[ $tt_id ] ] = array(); 3369 } 3370 $by_count[ $modifications[ $tt_id ] ][] = $tt_id; 3371 return $by_count; 3372 }, 3373 array() 3374 ); 3375 3376 foreach ( $tax_by_count as $_modify_by => $_tt_ids ) { 3377 wp_modify_term_count_by_now( $_tt_ids, $taxonomy_name, $_modify_by ); 3378 } 3379 unset( $_deferred[ $taxonomy_name ] ); 3380 } 3381 } 3382 3383 if ( empty( $tt_ids ) ) { 3384 return false; 3385 } 3386 3387 if ( ! is_array( $tt_ids ) ) { 3388 $tt_ids = array( $tt_ids ); 3389 } 3390 3391 if ( wp_defer_term_counting() ) { 3392 foreach ( $tt_ids as $tt_id ) { 3393 if ( ! isset( $_deferred[ $taxonomy ][ $tt_id ] ) ) { 3394 $_deferred[ $taxonomy ][ $tt_id ] = 0; 3395 } 3396 $_deferred[ $taxonomy ][ $tt_id ] += $modify_by; 3397 } 3398 return true; 3399 } 3400 3401 return wp_modify_term_count_by_now( $tt_ids, $taxonomy, $modify_by ); 3402 } 3403 3404 /** 3405 * Modifies the amount of terms in taxonomy immediately 3406 * 3407 * If there is a taxonomy callback applied, then it will be called for updating 3408 * the count. 3409 * 3410 * The default action is to decrement the count by one and update the database. 3411 * 3412 * @since 5.6.0 3413 * 3414 * @param int|array $tt_ids The term_taxonomy_id of the terms. 3415 * @param string $taxonomy The context of the term. 3416 * @param int $modify_by By how many the term count is to be modified. 3417 * @return bool If no terms will return false, and if successful will return true. 3418 */ 3419 function wp_modify_term_count_by_now( $tt_ids, $taxonomy, $modify_by ) { 3420 global $wpdb; 3421 3422 if ( 0 === $modify_by ) { 3423 return false; 3424 } 3425 3426 $tt_ids = array_filter( array_map( 'intval', (array) $tt_ids ) ); 3427 3428 if ( empty( $tt_ids ) ) { 3429 return false; 3430 } 3431 3432 $taxonomy = get_taxonomy( $taxonomy ); 3433 if ( ! empty( $taxonomy->update_count_by_callback ) ) { 3434 call_user_func( $taxonomy->update_count_by_callback, $tt_ids, $taxonomy, $modify_by ); 3435 clean_term_cache( $tt_ids, '', false ); 3436 return true; 3437 } 3438 3439 $tt_ids_string = '(' . implode( ',', $tt_ids ) . ')'; 3440 3441 foreach ( $tt_ids as $tt_id ) { 3442 /** This action is documented in wp-includes/taxonomy.php */ 3443 do_action( 'edit_term_taxonomy', $tt_id, $taxonomy ); 3444 } 3445 3446 $result = $wpdb->query( 3447 $wpdb->prepare( 3448 // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared 3449 "UPDATE {$wpdb->term_taxonomy} AS tt SET tt.count = GREATEST( 0, tt.count + %d ) WHERE tt.term_taxonomy_id IN $tt_ids_string", 3450 $modify_by 3451 ) 3452 ); 3453 3454 if ( ! $result ) { 3455 return false; 3456 } 3457 3458 foreach ( $tt_ids as $tt_id ) { 3459 /** This action is documented in wp-includes/taxonomy.php */ 3460 do_action( 'edited_term_taxonomy', $tt_id, $taxonomy ); 3461 } 3462 3463 clean_term_cache( $tt_ids, '', false ); 3464 3465 return true; 3466 } 3467 3236 3468 /** 3237 3469 * Updates the amount of terms in taxonomy. 3238 3470 * -
tests/phpunit/tests/term/termCounts.php
diff --git a/tests/phpunit/tests/term/termCounts.php b/tests/phpunit/tests/term/termCounts.php index 284d5f90ea..6e197c5d07 100644
a b class Tests_Term_termCount extends WP_UnitTestCase { 19 19 */ 20 20 public static $post_ids; 21 21 22 /** 23 * Array of tag IDs. 24 * 25 * @var int[] 26 */ 27 public static $tag_ids; 28 29 /** 30 * Term ID for testing user counts. 31 * 32 * @var int 33 */ 34 public static $user_term; 35 36 /** 37 * User ID for testing user counts. 38 * 39 * @var int 40 */ 41 public static $user_id; 42 22 43 /** 23 44 * Create shared fixtures. 24 45 * … … public static function wpSetUpBeforeClass( $factory ) { 30 51 self::$post_ids[ $status ] = $factory->post->create( array( 'post_status' => $status ) ); 31 52 } 32 53 33 register_taxonomy( 'wp_test_tax_counts', array( 'post', 'attachment' ) ); 54 // Extra published post. 55 self::$post_ids['publish_two'] = $factory->post->create( array( 'post_status' => 'publish' ) ); 56 57 self::$user_id = $factory->user->create( array( 'role' => 'author' ) ); 58 59 self::register_taxonomies(); 34 60 self::$attachment_term = $factory->term->create( array( 'taxonomy' => 'wp_test_tax_counts' ) ); 61 self::$user_term = $factory->term->create( array( 'taxonomy' => 'wp_test_user_tax_counts' ) ); 62 self::$tag_ids = $factory->term->create_many( 5 ); 35 63 } 36 64 37 65 public function setUp() { 38 66 parent::setUp(); 67 self::register_taxonomies(); 68 } 39 69 70 /** 71 * Register taxonomies used by tests. 72 * 73 * This is called both before class and before each test as the global is 74 * reset in each test's tearDown. 75 */ 76 public static function register_taxonomies() { 40 77 register_taxonomy( 'wp_test_tax_counts', array( 'post', 'attachment' ) ); 78 register_taxonomy( 'wp_test_user_tax_counts', 'user' ); 79 } 80 81 /** 82 * Term counts are not double incremented when post created. 83 * 84 * @covers wp_modify_term_count_by 85 * @dataProvider data_term_count_changes_for_post_statuses 86 * @ticket 40351 87 * 88 * @param string $post_status New post status. 89 * @param int $change Expected change. 90 */ 91 public function test_term_count_changes_for_post_statuses( $post_status, $change ) { 92 $term_count = get_term( get_option( 'default_category' ) )->count; 93 // Do not use shared fixture for this test as it relies on a new post. 94 $post_id = $this->factory()->post->create( array( 'post_status' => $post_status ) ); 95 96 $expected = $term_count + $change; 97 $this->assertSame( $expected, get_term( get_option( 'default_category' ) )->count ); 98 } 99 100 /** 101 * Data provider for test_term_count_changes_for_post_statuses. 102 * 103 * @return array[] { 104 * @type string $post_status New post status. 105 * @type int $change Expected change. 106 * } 107 */ 108 function data_term_count_changes_for_post_statuses() { 109 return array( 110 // 0. Published post 111 array( 'publish', 1 ), 112 // 1. Auto draft 113 array( 'auto-draft', 0 ), 114 // 2. Draft 115 array( 'draft', 0 ), 116 // 3. Private post 117 array( 'private', 0 ), 118 ); 41 119 } 42 120 43 121 /** 44 122 * Term counts increments correctly when post status becomes published. 45 123 * 46 124 * @covers wp_publish_post 47 * @covers wp_ count_terms125 * @covers wp_modify_term_count_by 48 126 * @dataProvider data_term_counts_incremented_on_publish 49 127 * @ticket 40351 50 128 * @ticket 51292 … … function data_term_counts_incremented_on_publish() { 83 161 ); 84 162 } 85 163 164 /** 165 * Test post status transition update term counts correctly. 166 * 167 * @covers wp_modify_term_count_by 168 * @dataProvider data_term_count_transitions_update_term_counts 169 * @ticket 40351 170 * 171 * @param string $original_post_status Post status upon create. 172 * @param string $new_post_status Post status after update. 173 * @param int $change Expected change upon publish. 174 */ 175 function test_term_count_transitions_update_term_counts( $original_post_status, $new_post_status, $change ) { 176 $post_id = self::$post_ids[ $original_post_status ]; 177 $term_count = get_term( get_option( 'default_category' ) )->count; 178 179 wp_update_post( 180 array( 181 'ID' => $post_id, 182 'post_status' => $new_post_status, 183 ) 184 ); 185 186 $expected = $term_count + $change; 187 $this->assertSame( $expected, get_term( get_option( 'default_category' ) )->count ); 188 } 189 190 /** 191 * Data provider for test_term_count_transitions_update_term_counts. 192 * 193 * @return array[] { 194 * @type string $original_post_status Post status upon create. 195 * @type string $new_post_status Post status after update. 196 * @type int $change Expected change upon publish. 197 * } 198 */ 199 function data_term_count_transitions_update_term_counts() { 200 return array( 201 // 0. Draft -> published post 202 array( 'draft', 'publish', 1 ), 203 // 1. Auto draft -> published post 204 array( 'auto-draft', 'publish', 1 ), 205 // 2. Private -> published post 206 array( 'private', 'publish', 1 ), 207 // 3. Published -> published post 208 array( 'publish', 'publish', 0 ), 209 210 // 4. Draft -> private post 211 array( 'draft', 'private', 0 ), 212 // 5. Auto draft -> private post 213 array( 'auto-draft', 'private', 0 ), 214 // 6. Private -> private post 215 array( 'private', 'private', 0 ), 216 // 7. Published -> private post 217 array( 'publish', 'private', -1 ), 218 219 // 8. Draft -> draft post 220 array( 'draft', 'draft', 0 ), 221 // 9. Auto draft -> draft post 222 array( 'auto-draft', 'draft', 0 ), 223 // 10. Private -> draft post 224 array( 'private', 'draft', 0 ), 225 // 11. Published -> draft post 226 array( 'publish', 'draft', -1 ), 227 ); 228 } 229 230 /** 231 * Term counts are not double incremented when post created. 232 * 233 * @covers wp_modify_term_count_by 234 * @dataProvider data_term_count_changes_for_post_statuses_with_attachments 235 * @ticket 40351 236 * 237 * @param string $post_status New post status. 238 * @param int $change Expected change. 239 */ 240 public function test_term_count_changes_for_post_statuses_with_attachments( $post_status, $change ) { 241 $term_count = get_term( self::$attachment_term )->count; 242 // Do not use shared fixture for this test as it relies on a new post. 243 $post_id = $this->factory()->post->create( array( 'post_status' => $post_status ) ); 244 wp_add_object_terms( $post_id, self::$attachment_term, 'wp_test_tax_counts' ); 245 $attachment_id = self::factory()->attachment->create_object( 246 array( 247 'file' => 'image.jpg', 248 'post_parent' => $post_id, 249 'post_status' => 'inherit', 250 ) 251 ); 252 wp_add_object_terms( $attachment_id, self::$attachment_term, 'wp_test_tax_counts' ); 253 254 $expected = $term_count + $change; 255 $this->assertSame( $expected, get_term( self::$attachment_term )->count ); 256 } 257 258 /** 259 * Data provider for test_term_count_changes_for_post_statuses_with_attachments. 260 * 261 * @return array[] { 262 * @type string $post_status New post status. 263 * @type int $change Expected change. 264 * } 265 */ 266 function data_term_count_changes_for_post_statuses_with_attachments() { 267 return array( 268 // 0. Published post 269 array( 'publish', 2 ), 270 // 1. Auto draft 271 array( 'auto-draft', 0 ), 272 // 2. Draft 273 array( 'draft', 0 ), 274 // 3. Private post 275 array( 'private', 0 ), 276 ); 277 } 278 86 279 /** 87 280 * Term counts increments correctly when post status becomes published. 88 281 * 89 282 * @covers wp_publish_post 283 * @covers wp_modify_term_count_by 90 284 * @dataProvider data_term_counts_incremented_on_publish_with_attachments 91 285 * @ticket 40351 92 286 * @ticket 51292 … … function data_term_counts_incremented_on_publish_with_attachments() { 134 328 ); 135 329 } 136 330 331 /** 332 * Test post status transition update term counts correctly. 333 * 334 * @covers wp_modify_term_count_by 335 * @dataProvider data_term_count_transitions_update_term_counts_with_attachments 336 * @ticket 40351 337 * 338 * @param string $original_post_status Post status upon create. 339 * @param string $new_post_status Post status after update. 340 * @param int $change Expected change upon publish. 341 */ 342 function test_term_count_transitions_update_term_counts_with_attachments( $original_post_status, $new_post_status, $change ) { 343 $post_id = self::$post_ids[ $original_post_status ]; 344 wp_add_object_terms( $post_id, self::$attachment_term, 'wp_test_tax_counts' ); 345 $attachment_id = self::factory()->attachment->create_object( 346 array( 347 'file' => 'image.jpg', 348 'post_parent' => $post_id, 349 'post_status' => 'inherit', 350 ) 351 ); 352 wp_add_object_terms( $attachment_id, self::$attachment_term, 'wp_test_tax_counts' ); 353 $term_count = get_term( self::$attachment_term )->count; 354 355 wp_update_post( 356 array( 357 'ID' => $post_id, 358 'post_status' => $new_post_status, 359 ) 360 ); 361 362 $expected = $term_count + $change; 363 $this->assertSame( $expected, get_term( self::$attachment_term )->count ); 364 } 365 366 /** 367 * Data provider for test_term_count_transitions_update_term_counts_with_attachments. 368 * 369 * @return array[] { 370 * @type string $original_post_status Post status upon create. 371 * @type string $new_post_status Post status after update. 372 * @type int $change Expected change upon publish. 373 * } 374 */ 375 function data_term_count_transitions_update_term_counts_with_attachments() { 376 return array( 377 // 0. Draft -> published post 378 array( 'draft', 'publish', 2 ), 379 // 1. Auto draft -> published post 380 array( 'auto-draft', 'publish', 2 ), 381 // 2. Private -> published post 382 array( 'private', 'publish', 2 ), 383 // 3. Published -> published post 384 array( 'publish', 'publish', 0 ), 385 386 // 4. Draft -> private post 387 array( 'draft', 'private', 0 ), 388 // 5. Auto draft -> private post 389 array( 'auto-draft', 'private', 0 ), 390 // 6. Private -> private post 391 array( 'private', 'private', 0 ), 392 // 7. Published -> private post 393 array( 'publish', 'private', -2 ), 394 395 // 8. Draft -> draft post 396 array( 'draft', 'draft', 0 ), 397 // 9. Auto draft -> draft post 398 array( 'auto-draft', 'draft', 0 ), 399 // 10. Private -> draft post 400 array( 'private', 'draft', 0 ), 401 // 11. Published -> draft post 402 array( 'publish', 'draft', -2 ), 403 ); 404 } 405 406 /** 407 * Term counts are not double incremented when post created. 408 * 409 * @covers wp_modify_term_count_by 410 * @dataProvider data_term_count_changes_for_post_statuses_with_untermed_attachments 411 * @ticket 40351 412 * 413 * @param string $post_status New post status. 414 * @param int $change Expected change. 415 */ 416 public function test_term_count_changes_for_post_statuses_with_untermed_attachments( $post_status, $change ) { 417 $term_count = get_term( self::$attachment_term )->count; 418 // Do not use shared fixture for this test as it relies on a new post. 419 $post_id = $this->factory()->post->create( array( 'post_status' => $post_status ) ); 420 wp_add_object_terms( $post_id, self::$attachment_term, 'wp_test_tax_counts' ); 421 $attachment_id = self::factory()->attachment->create_object( 422 array( 423 'file' => 'image.jpg', 424 'post_parent' => $post_id, 425 'post_status' => 'inherit', 426 ) 427 ); 428 429 $expected = $term_count + $change; 430 $this->assertSame( $expected, get_term( self::$attachment_term )->count ); 431 } 432 433 /** 434 * Data provider for test_term_count_changes_for_post_statuses_with_untermed_attachments. 435 * 436 * @return array[] { 437 * @type string $post_status New post status. 438 * @type int $change Expected change. 439 * } 440 */ 441 function data_term_count_changes_for_post_statuses_with_untermed_attachments() { 442 return array( 443 // 0. Published post 444 array( 'publish', 1 ), 445 // 1. Auto draft 446 array( 'auto-draft', 0 ), 447 // 2. Draft 448 array( 'draft', 0 ), 449 // 3. Private post 450 array( 'private', 0 ), 451 ); 452 } 453 137 454 /** 138 455 * Term counts increments correctly when post status becomes published. 139 456 * 457 * @covers wp_modify_term_count_by 140 458 * @covers wp_publish_post 141 459 * @dataProvider data_term_counts_incremented_on_publish_with_untermed_attachments 142 460 * @ticket 40351 … … function data_term_counts_incremented_on_publish_with_untermed_attachments() { 183 501 array( 'private', 1 ), 184 502 ); 185 503 } 504 505 /** 506 * Test post status transition update term counts correctly. 507 * 508 * @covers wp_modify_term_count_by 509 * @dataProvider data_term_count_transitions_update_term_counts_with_untermed_attachments 510 * @ticket 40351 511 * 512 * @param string $original_post_status Post status upon create. 513 * @param string $new_post_status Post status after update. 514 * @param int $change Expected change upon publish. 515 */ 516 function test_term_count_transitions_update_term_counts_with_untermed_attachments( $original_post_status, $new_post_status, $change ) { 517 $post_id = self::$post_ids[ $original_post_status ]; 518 wp_add_object_terms( $post_id, self::$attachment_term, 'wp_test_tax_counts' ); 519 $attachment_id = self::factory()->attachment->create_object( 520 array( 521 'file' => 'image.jpg', 522 'post_parent' => $post_id, 523 'post_status' => 'inherit', 524 ) 525 ); 526 $term_count = get_term( self::$attachment_term )->count; 527 528 wp_update_post( 529 array( 530 'ID' => $post_id, 531 'post_status' => $new_post_status, 532 ) 533 ); 534 535 $expected = $term_count + $change; 536 $this->assertSame( $expected, get_term( self::$attachment_term )->count ); 537 } 538 539 /** 540 * Data provider for test_term_count_transitions_update_term_counts_with_untermed_attachments. 541 * 542 * @return array[] { 543 * @type string $original_post_status Post status upon create. 544 * @type string $new_post_status Post status after update. 545 * @type int $change Expected change upon publish. 546 * } 547 */ 548 function data_term_count_transitions_update_term_counts_with_untermed_attachments() { 549 return array( 550 // 0. Draft -> published post 551 array( 'draft', 'publish', 1 ), 552 // 1. Auto draft -> published post 553 array( 'auto-draft', 'publish', 1 ), 554 // 2. Private -> published post 555 array( 'private', 'publish', 1 ), 556 // 3. Published -> published post 557 array( 'publish', 'publish', 0 ), 558 559 // 4. Draft -> private post 560 array( 'draft', 'private', 0 ), 561 // 5. Auto draft -> private post 562 array( 'auto-draft', 'private', 0 ), 563 // 6. Private -> private post 564 array( 'private', 'private', 0 ), 565 // 7. Published -> private post 566 array( 'publish', 'private', -1 ), 567 568 // 8. Draft -> draft post 569 array( 'draft', 'draft', 0 ), 570 // 9. Auto draft -> draft post 571 array( 'auto-draft', 'draft', 0 ), 572 // 10. Private -> draft post 573 array( 'private', 'draft', 0 ), 574 // 11. Published -> draft post 575 array( 'publish', 'draft', -1 ), 576 ); 577 } 578 579 /** 580 * User taxonomy term counts increments when added to an account. 581 * 582 * @covers wp_modify_term_count_by 583 * @ticket 51292 584 */ 585 public function test_term_counts_user_adding_term() { 586 $term_count = get_term( self::$user_term )->count; 587 wp_add_object_terms( self::$user_id, self::$user_term, 'wp_test_user_tax_counts' ); 588 589 $expected = $term_count + 1; 590 $this->assertSame( $expected, get_term( self::$user_term )->count ); 591 } 592 593 /** 594 * User taxonomy term counts decrement when term deleted from user. 595 * 596 * @covers wp_modify_term_count_by 597 * @ticket 51292 598 */ 599 public function test_term_counts_user_removing_term() { 600 wp_add_object_terms( self::$user_id, self::$user_term, 'wp_test_user_tax_counts' ); 601 $term_count = get_term( self::$user_term )->count; 602 603 wp_remove_object_terms( self::$user_id, self::$user_term, 'wp_test_user_tax_counts' ); 604 $expected = $term_count - 1; 605 $this->assertSame( $expected, get_term( self::$user_term )->count ); 606 } 607 608 /** 609 * Ensure DB queries for deferred counts are nullified for net zero gain. 610 * 611 * @covers wp_modify_term_count_by 612 * @covers wp_defer_term_counting 613 * @ticket 51292 614 */ 615 public function test_counts_after_deferral_net_zero() { 616 $post_one = self::$post_ids['publish']; 617 $post_two = self::$post_ids['publish_two']; 618 $terms = self::$tag_ids; 619 620 wp_set_object_terms( $post_one, $terms[0], 'post_tag', true ); 621 622 // Net gain 0; 623 wp_defer_term_counting( true ); 624 wp_remove_object_terms( $post_one, $terms[0], 'post_tag' ); 625 wp_set_object_terms( $post_two, $terms[0], 'post_tag', true ); 626 $num_queries = get_num_queries(); 627 wp_defer_term_counting( false ); 628 // Ensure number of queries unchanged. 629 $this->assertSame( $num_queries, get_num_queries() ); 630 } 631 632 /** 633 * Ensure full recounts follow modify by X recounts to avoid miscounts. 634 * 635 * @covers wp_modify_term_count_by 636 * @covers wp_update_term_count 637 * @covers wp_defer_term_counting 638 * @ticket 51292 639 */ 640 public function test_counts_after_deferral_full_before_partial() { 641 $post_one = self::$post_ids['publish']; 642 $terms = self::$tag_ids; 643 $term_count = get_term( $terms[0] )->count; 644 645 // Net gain 1; 646 wp_defer_term_counting( true ); 647 wp_set_object_terms( $post_one, $terms[0], 'post_tag', true ); 648 wp_update_term_count( get_term( $terms[0] )->term_taxonomy_id, 'post_tag' ); 649 wp_defer_term_counting( false ); 650 651 // Ensure term count is correct. 652 $expected = $term_count + 1; 653 $this->assertSame( $expected, get_term( $terms[0] )->count ); 654 } 655 656 /** 657 * Ensure DB queries for deferred counts are combined. 658 * 659 * @covers wp_modify_term_count_by 660 * @covers wp_defer_term_counting 661 * @ticket 51292 662 */ 663 public function test_counts_after_deferral_matching_changes() { 664 $post_one = self::$post_ids['publish']; 665 $post_two = self::$post_ids['publish_two']; 666 $terms = self::$tag_ids; 667 668 wp_set_object_terms( $post_one, $terms[0], 'post_tag', true ); 669 670 // Net gain 0: 671 wp_defer_term_counting( true ); 672 wp_remove_object_terms( $post_one, $terms[0], 'post_tag' ); 673 wp_set_object_terms( $post_two, $terms[0], 'post_tag', true ); 674 675 // Net gain 1: 676 wp_set_object_terms( $post_one, $terms[1], 'post_tag', true ); 677 wp_set_object_terms( $post_two, $terms[2], 'post_tag', true ); 678 679 // Net gain 2: 680 wp_set_object_terms( $post_one, array( $terms[3], $terms[4] ), 'post_tag', true ); 681 wp_set_object_terms( $post_two, array( $terms[3], $terms[4] ), 'post_tag', true ); 682 683 $num_queries = get_num_queries(); 684 wp_defer_term_counting( false ); 685 686 /* 687 * Each count is expected to produce two queries: 688 * 1) The count update 689 * 2) The SELECT in `clean_term_cache()`. 690 */ 691 $expected = $num_queries + ( 2 * 2 ); 692 // Ensure number of queries correct. 693 $this->assertSame( $expected, get_num_queries() ); 694 } 186 695 }