Make WordPress Core

Changeset 60711


Ignore:
Timestamp:
09/05/2025 01:28:20 PM (11 months ago)
Author:
SergeyBiryukov
Message:

Taxonomy: Add update_term_count action that fires when term counts are updated.

This allows plugins to run custom queries only when a term count is actually updated and not on every update of terms or posts.

Follow-up to [60365], [60510].

Props leonidasmilossis, peterwilsoncc, mukesh27, rollybueno, SergeyBiryukov.
Fixes #63904.

Location:
trunk
Files:
2 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/wp-includes/taxonomy.php

    r60697 r60711  
    41764176        $post_statuses = esc_sql( apply_filters( 'update_post_term_count_statuses', $post_statuses, $taxonomy ) );
    41774177
    4178         foreach ( (array) $terms as $term ) {
     4178        foreach ( (array) $terms as $tt_id ) {
    41794179                $count = 0;
    41804180
     
    41824182                if ( $check_attachments ) {
    41834183                        // phpcs:ignore WordPress.DB.PreparedSQLPlaceholders.QuotedDynamicPlaceholderGeneration
    4184                         $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 IN ('" . implode( "', '", $post_statuses ) . "') OR ( post_status = 'inherit' AND post_parent > 0 AND ( SELECT post_status FROM $wpdb->posts WHERE ID = p1.post_parent ) IN ('" . implode( "', '", $post_statuses ) . "') ) ) AND post_type = 'attachment' AND term_taxonomy_id = %d", $term ) );
     4184                        $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 IN ('" . implode( "', '", $post_statuses ) . "') OR ( post_status = 'inherit' AND post_parent > 0 AND ( SELECT post_status FROM $wpdb->posts WHERE ID = p1.post_parent ) IN ('" . implode( "', '", $post_statuses ) . "') ) ) AND post_type = 'attachment' AND term_taxonomy_id = %d", $tt_id ) );
    41854185                }
    41864186
    41874187                if ( $object_types ) {
    41884188                        // phpcs:ignore WordPress.DB.PreparedSQLPlaceholders.QuotedDynamicPlaceholderGeneration
    4189                         $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 IN ('" . implode( "', '", $post_statuses ) . "') AND post_type IN ('" . implode( "', '", $object_types ) . "') AND term_taxonomy_id = %d", $term ) );
    4190                 }
     4189                        $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 IN ('" . implode( "', '", $post_statuses ) . "') AND post_type IN ('" . implode( "', '", $object_types ) . "') AND term_taxonomy_id = %d", $tt_id ) );
     4190                }
     4191
     4192                /**
     4193                 * Fires when a term count is calculated, before it is updated in the database.
     4194                 *
     4195                 * @since 6.9.0
     4196                 *
     4197                 * @param int    $tt_id         Term taxonomy ID.
     4198                 * @param string $taxonomy_name Taxonomy slug.
     4199                 * @param int    $count         Term count.
     4200                 */
     4201                do_action( 'update_term_count', $tt_id, $taxonomy->name, $count );
    41914202
    41924203                /** This action is documented in wp-includes/taxonomy.php */
    4193                 do_action( 'edit_term_taxonomy', $term, $taxonomy->name );
    4194                 $wpdb->update( $wpdb->term_taxonomy, compact( 'count' ), array( 'term_taxonomy_id' => $term ) );
     4204                do_action( 'edit_term_taxonomy', $tt_id, $taxonomy->name );
     4205                $wpdb->update( $wpdb->term_taxonomy, compact( 'count' ), array( 'term_taxonomy_id' => $tt_id ) );
    41954206
    41964207                /** This action is documented in wp-includes/taxonomy.php */
    4197                 do_action( 'edited_term_taxonomy', $term, $taxonomy->name );
     4208                do_action( 'edited_term_taxonomy', $tt_id, $taxonomy->name );
    41984209        }
    41994210}
     
    42164227        foreach ( (array) $terms as $term ) {
    42174228                $count = $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(*) FROM $wpdb->term_relationships WHERE term_taxonomy_id = %d", $term ) );
     4229
     4230                /** This action is documented in wp-includes/taxonomy.php */
     4231                do_action( 'update_term_count', $term, $taxonomy->name, $count );
    42184232
    42194233                /** This action is documented in wp-includes/taxonomy.php */
  • trunk/tests/phpunit/tests/post/updateTermCountOnTransitionPostStatus.php

    r60510 r60711  
    22
    33/**
    4  * Tests for the _update_term_count_on_transition_post_status function.
     4 * Tests for the `_update_term_count_on_transition_post_status()` function.
    55 *
    6  * See Tests_Term_WpSetObjectTerms for tests that cover changing terms on a post when saving it.
     6 * See `Tests_Term_WpSetObjectTerms` for tests that cover changing terms on a post when saving it.
    77 *
    88 * @group taxonomy
     
    140140         */
    141141        public function test_term_count_is_not_recalculated_when_status_does_not_change() {
    142                 // Create a mock action for `edited_term_taxonomy` to prevent flaky test.
    143                 $action = new MockAction();
    144                 add_action( 'edited_term_taxonomy', array( $action, 'action' ) );
     142                // Create a mock action for checking the `edited_term_taxonomy` hook call count.
     143                $edited_term_taxonomy_action = new MockAction();
     144                add_action( 'edited_term_taxonomy', array( $edited_term_taxonomy_action, 'action' ) );
     145
     146                // Create a mock action for checking the `update_term_count` hook call count.
     147                $update_term_count_action = new MockAction();
     148                add_action( 'update_term_count', array( $update_term_count_action, 'action' ) );
    145149
    146150                $post_id = self::factory()->post->create(
     
    156160                        self::$taxonomy
    157161                );
    158                 $edited_term_taxonomy_count = $action->get_call_count();
     162                $edited_term_taxonomy_count     = $edited_term_taxonomy_action->get_call_count();
     163                $update_term_count_action_count = $update_term_count_action->get_call_count();
    159164
    160165                // Change something about the post but not its status.
     
    166171                );
    167172
    168                 $this->assertSame( 0, $action->get_call_count() - $edited_term_taxonomy_count, 'Term taxonomy count should not be recalculated when post status does not change.' );
     173                $this->assertSame( 0, $edited_term_taxonomy_action->get_call_count() - $edited_term_taxonomy_count, 'Term taxonomy count should not be recalculated when post status does not change.' );
     174                $this->assertSame( 0, $update_term_count_action->get_call_count() - $update_term_count_action_count, 'The `update_term_count` action should not run when term taxonomy count is not recalculated.' );
    169175                $this->assertTermCount( 2, self::$term_id );
    170176        }
     
    178184         */
    179185        public function test_term_count_is_not_recalculated_when_both_status_are_counted() {
    180                 // Create a mock action for `edited_term_taxonomy` to prevent flaky test.
    181                 $action = new MockAction();
    182                 add_action( 'edited_term_taxonomy', array( $action, 'action' ) );
     186                // Create a mock action for checking the `edited_term_taxonomy` hook call count.
     187                $edited_term_taxonomy_action = new MockAction();
     188                add_action( 'edited_term_taxonomy', array( $edited_term_taxonomy_action, 'action' ) );
     189
     190                // Create a mock action for checking the `update_term_count` hook call count.
     191                $update_term_count_action = new MockAction();
     192                add_action( 'update_term_count', array( $update_term_count_action, 'action' ) );
    183193
    184194                // Register a custom status that is included in term counts.
     
    207217                );
    208218
    209                 $this->assertSame( 0, $action->get_call_count(), 'Term taxonomy count should not be recalculated both statuses are included in term counts.' );
     219                $this->assertSame( 0, $edited_term_taxonomy_action->get_call_count(), 'Term taxonomy count should not be recalculated when both statuses are included in term counts.' );
     220                $this->assertSame( 0, $update_term_count_action->get_call_count(), 'The `update_term_count` action should not run when term taxonomy count is not recalculated.' );
    210221                $this->assertTermCount( 1, self::$term_id, 'Term count should remain unchanged when transitioning between post statuses that are counted.' );
    211222        }
     
    219230         */
    220231        public function test_term_count_is_not_recalculated_when_neither_status_is_counted() {
    221                 // Create a mock action for `edited_term_taxonomy` to prevent flaky test.
    222                 $action = new MockAction();
    223                 add_action( 'edited_term_taxonomy', array( $action, 'action' ) );
     232                // Create a mock action for checking the `edited_term_taxonomy` hook call count.
     233                $edited_term_taxonomy_action = new MockAction();
     234                add_action( 'edited_term_taxonomy', array( $edited_term_taxonomy_action, 'action' ) );
     235
     236                // Create a mock action for checking the `update_term_count` hook call count.
     237                $update_term_count_action = new MockAction();
     238                add_action( 'update_term_count', array( $update_term_count_action, 'action' ) );
    224239
    225240                // Change post status to draft.
     
    231246                );
    232247
    233                 $edited_term_taxonomy_count = $action->get_call_count();
     248                $edited_term_taxonomy_count     = $edited_term_taxonomy_action->get_call_count();
     249                $update_term_count_action_count = $update_term_count_action->get_call_count();
    234250
    235251                // Change the post to another status that is not included in term counts.
     
    241257                );
    242258
    243                 $this->assertSame( 0, $action->get_call_count() - $edited_term_taxonomy_count, 'Term taxonomy count should not be recalculated when neither new nor old post status is included in term counts.' );
     259                $this->assertSame( 0, $edited_term_taxonomy_action->get_call_count() - $edited_term_taxonomy_count, 'Term taxonomy count should not be recalculated when neither new nor old post status is included in term counts.' );
     260                $this->assertSame( 0, $update_term_count_action->get_call_count() - $update_term_count_action_count, 'The `update_term_count` action should not run when term taxonomy count is not recalculated.' );
    244261                $this->assertTermCount( 0, self::$term_id, 'Term count should remain unchanged when transitioning between post statuses that are not counted.' );
    245262        }
     
    251268         */
    252269        public function test_update_post_term_count_statuses_filter_is_respected() {
    253                 // Create a mock action for `edited_term_taxonomy` to prevent flaky test.
    254                 $action = new MockAction();
    255                 add_action( 'edited_term_taxonomy', array( $action, 'action' ) );
     270                // Create a mock action for checking the `edited_term_taxonomy` hook call count.
     271                $edited_term_taxonomy_action = new MockAction();
     272                add_action( 'edited_term_taxonomy', array( $edited_term_taxonomy_action, 'action' ) );
     273
     274                // Create a mock action for checking the `update_term_count` hook call count.
     275                $update_term_count_action = new MockAction();
     276                add_action( 'update_term_count', array( $update_term_count_action, 'action' ) );
    256277
    257278                $custom_taxonomy = 'category_with_pending';
     
    294315                );
    295316
    296                 $edited_term_taxonomy_count = $action->get_call_count();
     317                $edited_term_taxonomy_count     = $edited_term_taxonomy_action->get_call_count();
     318                $update_term_count_action_count = $update_term_count_action->get_call_count();
    297319
    298320                // Change the post to another status that is included in term counts for one of its two taxonomies.
     
    304326                );
    305327
    306                 $this->assertSame( 1, $action->get_call_count() - $edited_term_taxonomy_count, 'Term taxonomy count should respect the statuses returned by the update_post_term_count_statuses filter.' );
     328                $this->assertSame( 1, $edited_term_taxonomy_action->get_call_count() - $edited_term_taxonomy_count, 'Term taxonomy count should respect the statuses returned by the update_post_term_count_statuses filter.' );
     329                $this->assertSame( 1, $update_term_count_action->get_call_count() - $update_term_count_action_count, 'The `update_term_count` action should run when term taxonomy count is recalculated.' );
    307330                $this->assertTermCount( 0, self::$term_id, 'Term count for the default taxonomy should remain zero since "pending" is not included in its countable statuses.' );
    308331                $this->assertTermCount( 1, $custom_term_id, 'Term count for the custom taxonomy should be updated to 1 because the "pending" status is included via the update_post_term_count_statuses filter.' );
Note: See TracChangeset for help on using the changeset viewer.