Skip to content

Commit dda73c9

Browse files
CopilotswissspidyCopilot
authored
Add wp term prune command to remove terms with 0 or 1 published posts (#586)
Co-authored-by: swissspidy <841956+swissspidy@users.noreply.github.com> Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: Pascal Birchler <pascalb@google.com>
1 parent b631d41 commit dda73c9

File tree

3 files changed

+228
-0
lines changed

3 files changed

+228
-0
lines changed

composer.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -209,6 +209,7 @@
209209
"term meta update",
210210
"term migrate",
211211
"term recount",
212+
"term prune",
212213
"term update",
213214
"user",
214215
"user add-cap",

features/term-prune.feature

Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
Feature: Prune unused taxonomy terms
2+
3+
Background:
4+
Given a WP install
5+
6+
Scenario: Prune terms with no published posts
7+
When I run `wp term create post_tag 'Unused Tag' --slug=unused-tag --porcelain`
8+
Then STDOUT should be a number
9+
And save STDOUT as {TERM_ID}
10+
11+
When I run `wp term prune post_tag`
12+
Then STDOUT should contain:
13+
"""
14+
Deleted post_tag {TERM_ID}.
15+
"""
16+
And STDOUT should contain:
17+
"""
18+
Success:
19+
"""
20+
And the return code should be 0
21+
22+
When I try `wp term get post_tag {TERM_ID}`
23+
Then STDERR should contain:
24+
"""
25+
Error: Term doesn't exist.
26+
"""
27+
28+
Scenario: Does not prune terms with more than one published post
29+
When I run `wp term create post_tag 'Popular Tag' --slug=popular-tag --porcelain`
30+
Then STDOUT should be a number
31+
And save STDOUT as {TERM_ID}
32+
33+
When I run `wp post create --post_title='Post 1' --post_status=publish --porcelain`
34+
Then STDOUT should be a number
35+
And save STDOUT as {POST_ID_1}
36+
37+
When I run `wp post create --post_title='Post 2' --post_status=publish --porcelain`
38+
Then STDOUT should be a number
39+
And save STDOUT as {POST_ID_2}
40+
41+
When I run `wp post term set {POST_ID_1} post_tag {TERM_ID} --by=id`
42+
Then STDOUT should not be empty
43+
44+
When I run `wp post term set {POST_ID_2} post_tag {TERM_ID} --by=id`
45+
Then STDOUT should not be empty
46+
47+
When I run `wp term prune post_tag`
48+
Then STDOUT should not contain:
49+
"""
50+
Deleted post_tag {TERM_ID}.
51+
"""
52+
53+
When I run `wp term get post_tag {TERM_ID} --field=name`
54+
Then STDOUT should be:
55+
"""
56+
Popular Tag
57+
"""
58+
59+
Scenario: Prune terms with exactly one published post
60+
When I run `wp term create post_tag 'Single Post Tag' --slug=single-post-tag --porcelain`
61+
Then STDOUT should be a number
62+
And save STDOUT as {TERM_ID}
63+
64+
When I run `wp post create --post_title='Post 1' --post_status=publish --porcelain`
65+
Then STDOUT should be a number
66+
And save STDOUT as {POST_ID}
67+
68+
When I run `wp post term set {POST_ID} post_tag {TERM_ID} --by=id`
69+
Then STDOUT should not be empty
70+
71+
When I run `wp term prune post_tag`
72+
Then STDOUT should contain:
73+
"""
74+
Deleted post_tag {TERM_ID}.
75+
"""
76+
And the return code should be 0
77+
78+
When I try `wp term get post_tag {TERM_ID}`
79+
Then STDERR should contain:
80+
"""
81+
Error: Term doesn't exist.
82+
"""
83+
84+
Scenario: Dry run previews terms without deleting them
85+
When I run `wp term create post_tag 'Unused Tag' --slug=unused-tag --porcelain`
86+
Then STDOUT should be a number
87+
And save STDOUT as {TERM_ID}
88+
89+
When I run `wp term prune post_tag --dry-run`
90+
Then STDOUT should contain:
91+
"""
92+
Would delete post_tag {TERM_ID}.
93+
"""
94+
And STDOUT should contain:
95+
"""
96+
Success:
97+
"""
98+
And the return code should be 0
99+
100+
When I run `wp term get post_tag {TERM_ID} --field=name`
101+
Then STDOUT should be:
102+
"""
103+
Unused Tag
104+
"""
105+
106+
Scenario: Prune with an invalid taxonomy
107+
When I try `wp term prune nonexistent_taxonomy`
108+
Then STDERR should be:
109+
"""
110+
Error: Taxonomy nonexistent_taxonomy doesn't exist.
111+
"""
112+
And the return code should be 1
113+
114+
Scenario: Prune multiple taxonomies at once
115+
# Assign an extra post to the default Uncategorized category so its count
116+
# exceeds the prune threshold and it won't interfere with the test.
117+
When I run `wp post create --post_title='Extra Post' --post_status=publish --post_category=1 --porcelain`
118+
Then STDOUT should be a number
119+
120+
When I run `wp term create post_tag 'Unused Tag' --slug=unused-tag --porcelain`
121+
Then STDOUT should be a number
122+
And save STDOUT as {TAG_TERM_ID}
123+
124+
When I run `wp term create category 'Unused Category' --slug=unused-category --porcelain`
125+
Then STDOUT should be a number
126+
And save STDOUT as {CAT_TERM_ID}
127+
128+
When I run `wp term prune post_tag category`
129+
Then STDOUT should contain:
130+
"""
131+
Deleted post_tag {TAG_TERM_ID}.
132+
"""
133+
And STDOUT should contain:
134+
"""
135+
Deleted category {CAT_TERM_ID}.
136+
"""
137+
And the return code should be 0

src/Term_Command.php

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,11 @@
3535
* Success: Updated category term count
3636
* Success: Updated post_tag term count
3737
*
38+
* # Prune terms with 0 or 1 published posts
39+
* $ wp term prune post_tag
40+
* Deleted post_tag 15.
41+
* Success: Pruned 1 of 5 terms.
42+
*
3843
* @package wp-cli
3944
*/
4045
class Term_Command extends WP_CLI_Command {
@@ -682,6 +687,91 @@ public function recount( $args ) {
682687
}
683688
}
684689

690+
/**
691+
* Removes terms with 0 or 1 published posts from one or more taxonomies.
692+
*
693+
* Useful for cleaning up large sites with many unused or barely-used terms.
694+
* The term count is based on the number of published posts assigned to each
695+
* term.
696+
*
697+
* ## OPTIONS
698+
*
699+
* <taxonomy>...
700+
* : One or more taxonomies to prune.
701+
*
702+
* [--dry-run]
703+
* : Preview the terms to be pruned, without actually deleting them.
704+
*
705+
* ## EXAMPLES
706+
*
707+
* # Prune post tags with 0 or 1 published posts.
708+
* $ wp term prune post_tag
709+
* Deleted post_tag 15.
710+
* Success: Pruned 1 of 5 terms.
711+
*
712+
* # Dry run to preview which terms would be pruned.
713+
* $ wp term prune post_tag --dry-run
714+
* Would delete post_tag 15.
715+
* Success: 1 post_tag term would be pruned.
716+
*
717+
* # Prune multiple taxonomies at once.
718+
* $ wp term prune category post_tag
719+
* Deleted category 8.
720+
* Success: Pruned 1 of 3 terms.
721+
* Deleted post_tag 15.
722+
* Success: Pruned 1 of 5 terms.
723+
*/
724+
public function prune( $args, $assoc_args ) {
725+
global $wpdb;
726+
727+
$dry_run = (bool) Utils\get_flag_value( $assoc_args, 'dry-run', false );
728+
729+
foreach ( $args as $taxonomy ) {
730+
if ( ! taxonomy_exists( $taxonomy ) ) {
731+
WP_CLI::error( "Taxonomy {$taxonomy} doesn't exist." );
732+
}
733+
734+
$term_ids_to_prune = $wpdb->get_col(
735+
$wpdb->prepare(
736+
"SELECT term_id FROM {$wpdb->term_taxonomy} WHERE taxonomy = %s AND count <= 1",
737+
$taxonomy
738+
)
739+
);
740+
741+
$total = count( $term_ids_to_prune );
742+
$successes = 0;
743+
$errors = 0;
744+
745+
foreach ( $term_ids_to_prune as $term_id ) {
746+
if ( $dry_run ) {
747+
WP_CLI::log( "Would delete {$taxonomy} {$term_id}." );
748+
++$successes;
749+
continue;
750+
}
751+
752+
$result = wp_delete_term( $term_id, $taxonomy );
753+
754+
if ( is_wp_error( $result ) ) {
755+
WP_CLI::warning( $result );
756+
++$errors;
757+
} elseif ( $result ) {
758+
WP_CLI::log( "Deleted {$taxonomy} {$term_id}." );
759+
++$successes;
760+
} else {
761+
WP_CLI::warning( "Term {$term_id} in taxonomy {$taxonomy} doesn't exist." );
762+
++$errors;
763+
}
764+
}
765+
766+
if ( $dry_run ) {
767+
$term_word = Utils\pluralize( 'term', $successes );
768+
WP_CLI::success( "{$successes} {$taxonomy} {$term_word} would be pruned." );
769+
} else {
770+
Utils\report_batch_operation_results( 'term', 'prune', $total, $successes, $errors );
771+
}
772+
}
773+
}
774+
685775
/**
686776
* Migrate a term of a taxonomy to another taxonomy.
687777
*

0 commit comments

Comments
 (0)