Skip to content

Commit c15303c

Browse files
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. git-svn-id: https://develop.svn.wordpress.org/trunk@60711 602fd350-edb4-49c9-b593-d223f7449a82
1 parent e81ed11 commit c15303c

File tree

2 files changed

+64
-27
lines changed

2 files changed

+64
-27
lines changed

src/wp-includes/taxonomy.php

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4175,26 +4175,37 @@ function _update_post_term_count( $terms, $taxonomy ) {
41754175
*/
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

41814181
// Attachments can be 'inherit' status, we need to base count off the parent's status if so.
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 ) );
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 ) );
41904190
}
41914191

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 );
4202+
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
}
42004211

@@ -4216,6 +4227,9 @@ function _update_generic_term_count( $terms, $taxonomy ) {
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 ) );
42184229

4230+
/** This action is documented in wp-includes/taxonomy.php */
4231+
do_action( 'update_term_count', $term, $taxonomy->name, $count );
4232+
42194233
/** This action is documented in wp-includes/taxonomy.php */
42204234
do_action( 'edit_term_taxonomy', $term, $taxonomy->name );
42214235
$wpdb->update( $wpdb->term_taxonomy, compact( 'count' ), array( 'term_taxonomy_id' => $term ) );

tests/phpunit/tests/post/updateTermCountOnTransitionPostStatus.php

Lines changed: 44 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
<?php
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
99
*
@@ -139,9 +139,13 @@ 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' ) );
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(
147151
array(
@@ -155,7 +159,8 @@ public function test_term_count_is_not_recalculated_when_status_does_not_change(
155159
self::$term_id,
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.
161166
wp_update_post(
@@ -165,7 +170,8 @@ public function test_term_count_is_not_recalculated_when_status_does_not_change(
165170
)
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
}
171177

@@ -177,9 +183,13 @@ public function test_term_count_is_not_recalculated_when_status_does_not_change(
177183
* @ticket 63562
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.
185195
register_post_status(
@@ -206,7 +216,8 @@ static function ( $status ) {
206216
)
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
}
212223

@@ -218,9 +229,13 @@ static function ( $status ) {
218229
* @ticket 63562
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.
226241
wp_update_post(
@@ -230,7 +245,8 @@ public function test_term_count_is_not_recalculated_when_neither_status_is_count
230245
)
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.
236252
wp_update_post(
@@ -240,7 +256,8 @@ public function test_term_count_is_not_recalculated_when_neither_status_is_count
240256
)
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
}
246263

@@ -250,9 +267,13 @@ public function test_term_count_is_not_recalculated_when_neither_status_is_count
250267
* @ticket 63562
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';
258279

@@ -293,7 +314,8 @@ static function ( array $statuses, WP_Taxonomy $taxonomy ) use ( $custom_taxonom
293314
$custom_taxonomy
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.
299321
wp_update_post(
@@ -303,7 +325,8 @@ static function ( array $statuses, WP_Taxonomy $taxonomy ) use ( $custom_taxonom
303325
)
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.' );
309332
}

0 commit comments

Comments
 (0)