Make WordPress Core


Ignore:
Timestamp:
07/25/2025 08:35:12 AM (4 months ago)
Author:
johnbillion
Message:

Posts, Post Types: Don't unnecessarily recount terms when a post transitions between statuses that don't require them to be recounted.

This accounts for transitions where:

  • Both the old and new statuses are not included in term counts.
  • Both the old and new statuses are included in term counts.

This results in term counts only being recalculated when a post transitions between a counted and an uncounted status.

If the terms of the post are changed then the term recounting is still handled by wp_update_term_count() inside wp_set_object_terms().

Props hbhalodia, johnbillion, peterwilsoncc, mukesh27.

Fixes #63562

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/tests/phpunit/tests/post/updateTermCountOnTransitionPostStatus.php

    r60365 r60510  
    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' ) );
     145
    142146        $post_id = self::factory()->post->create(
    143147            array(
     
    152156            self::$taxonomy
    153157        );
    154         $edited_term_taxonomy_count = did_action( 'edited_term_taxonomy' );
     158        $edited_term_taxonomy_count = $action->get_call_count();
    155159
    156160        // Change something about the post but not its status.
     
    162166        );
    163167
    164         $this->assertSame( 0, did_action( 'edited_term_taxonomy' ) - $edited_term_taxonomy_count, 'Term taxonomy count should not be recalculated when post status does not change.' );
     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.' );
    165169        $this->assertTermCount( 2, self::$term_id );
     170    }
     171
     172    /**
     173     * Test that the term count is not recalculated when both the old and new status are included in term counts.
     174     *
     175     * This accounts for a transition such as draft -> pending.
     176     *
     177     * @ticket 63562
     178     */
     179    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' ) );
     183
     184        // Register a custom status that is included in term counts.
     185        register_post_status(
     186            'counted',
     187            array(
     188                'label'  => 'Counted',
     189                'public' => true,
     190            )
     191        );
     192
     193        add_filter(
     194            'update_post_term_count_statuses',
     195            static function ( $status ) {
     196                $status[] = 'counted';
     197                return $status;
     198            }
     199        );
     200
     201        // Change the post to another status that is included in term counts.
     202        wp_update_post(
     203            array(
     204                'ID'          => self::$post_id,
     205                'post_status' => 'counted',
     206            )
     207        );
     208
     209        $this->assertSame( 0, $action->get_call_count(), 'Term taxonomy count should not be recalculated both statuses are included in term counts.' );
     210        $this->assertTermCount( 1, self::$term_id, 'Term count should remain unchanged when transitioning between post statuses that are counted.' );
     211    }
     212
     213    /**
     214     * Test that the term count is not recalculated when neither the old nor new status are included in term counts.
     215     *
     216     * This accounts for a transition such as draft -> pending.
     217     *
     218     * @ticket 63562
     219     */
     220    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' ) );
     224
     225        // Change post status to draft.
     226        wp_update_post(
     227            array(
     228                'ID'          => self::$post_id,
     229                'post_status' => 'draft',
     230            )
     231        );
     232
     233        $edited_term_taxonomy_count = $action->get_call_count();
     234
     235        // Change the post to another status that is not included in term counts.
     236        wp_update_post(
     237            array(
     238                'ID'          => self::$post_id,
     239                'post_status' => 'pending',
     240            )
     241        );
     242
     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.' );
     244        $this->assertTermCount( 0, self::$term_id, 'Term count should remain unchanged when transitioning between post statuses that are not counted.' );
     245    }
     246
     247    /**
     248     * Test to ensure that the `update_post_term_count_statuses` filter is respected.
     249     *
     250     * @ticket 63562
     251     */
     252    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' ) );
     256
     257        $custom_taxonomy = 'category_with_pending';
     258
     259        // Add a custom taxonomy that includes 'pending' in its term counts.
     260        register_taxonomy(
     261            $custom_taxonomy,
     262            self::$post_type
     263        );
     264        add_filter(
     265            'update_post_term_count_statuses',
     266            static function ( array $statuses, WP_Taxonomy $taxonomy ) use ( $custom_taxonomy ): array {
     267                if ( $custom_taxonomy === $taxonomy->name ) {
     268                    $statuses[] = 'pending';
     269                }
     270
     271                return $statuses;
     272            },
     273            10,
     274            2
     275        );
     276
     277        // Change post status to draft and give it a term to count.
     278        wp_update_post(
     279            array(
     280                'ID'          => self::$post_id,
     281                'post_status' => 'draft',
     282            )
     283        );
     284        $custom_term_id = self::factory()->term->create(
     285            array(
     286                'taxonomy' => $custom_taxonomy,
     287                'name'     => 'Hello',
     288            )
     289        );
     290        wp_set_object_terms(
     291            self::$post_id,
     292            $custom_term_id,
     293            $custom_taxonomy
     294        );
     295
     296        $edited_term_taxonomy_count = $action->get_call_count();
     297
     298        // Change the post to another status that is included in term counts for one of its two taxonomies.
     299        wp_update_post(
     300            array(
     301                'ID'          => self::$post_id,
     302                'post_status' => 'pending',
     303            )
     304        );
     305
     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.' );
     307        $this->assertTermCount( 0, self::$term_id, 'Term count for the default taxonomy should remain zero since "pending" is not included in its countable statuses.' );
     308        $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.' );
    166309    }
    167310
Note: See TracChangeset for help on using the changeset viewer.