Skip to content

Commit 981086b

Browse files
committed
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 git-svn-id: https://develop.svn.wordpress.org/trunk@60510 602fd350-edb4-49c9-b593-d223f7449a82
1 parent 857852f commit 981086b

File tree

2 files changed

+169
-5
lines changed

2 files changed

+169
-5
lines changed

src/wp-includes/post.php

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8175,9 +8175,30 @@ function _update_term_count_on_transition_post_status( $new_status, $old_status,
81758175
}
81768176

81778177
// Update counts for the post's terms.
8178-
foreach ( (array) get_object_taxonomies( $post->post_type ) as $taxonomy ) {
8179-
$tt_ids = wp_get_object_terms( $post->ID, $taxonomy, array( 'fields' => 'tt_ids' ) );
8180-
wp_update_term_count( $tt_ids, $taxonomy );
8178+
foreach ( (array) get_object_taxonomies( $post->post_type, 'objects' ) as $taxonomy ) {
8179+
/** This filter is documented in wp-includes/taxonomy.php */
8180+
$counted_statuses = apply_filters( 'update_post_term_count_statuses', array( 'publish' ), $taxonomy );
8181+
8182+
/*
8183+
* Do not recalculate term count if both the old and new status are not included in term counts.
8184+
* This accounts for a transition such as draft -> pending.
8185+
*/
8186+
if ( ! in_array( $old_status, $counted_statuses, true ) && ! in_array( $new_status, $counted_statuses, true ) ) {
8187+
continue;
8188+
}
8189+
8190+
/*
8191+
* Do not recalculate term count if both the old and new status are included in term counts.
8192+
*
8193+
* This accounts for transitioning between statuses which are both included in term counts. This can only occur
8194+
* if the `update_post_term_count_statuses` filter is in use to count more than just the 'publish' status.
8195+
*/
8196+
if ( in_array( $old_status, $counted_statuses, true ) && in_array( $new_status, $counted_statuses, true ) ) {
8197+
continue;
8198+
}
8199+
8200+
$tt_ids = wp_get_object_terms( $post->ID, $taxonomy->name, array( 'fields' => 'tt_ids' ) );
8201+
wp_update_term_count( $tt_ids, $taxonomy->name );
81818202
}
81828203
}
81838204

tests/phpunit/tests/post/updateTermCountOnTransitionPostStatus.php

Lines changed: 145 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,10 @@ public function test_update_term_count_on_delete() {
139139
* @ticket 42522
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(
144148
'post_type' => self::$post_type,
@@ -151,7 +155,7 @@ public function test_term_count_is_not_recalculated_when_status_does_not_change(
151155
self::$term_id,
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.
157161
wp_update_post(
@@ -161,10 +165,149 @@ public function test_term_count_is_not_recalculated_when_status_does_not_change(
161165
)
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 );
166170
}
167171

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.' );
309+
}
310+
168311
/**
169312
* Assert that the term count is correct.
170313
*

0 commit comments

Comments
 (0)