ワイヤード・パンチ

元・大阪人が、岡山の山奥でも生きていけることを証明するためのブログ。

WordPressのget_termsで、投稿数が0のタームだけを一括で抽出する方法。

仕事のうえで作ってるサイトにて、なぜか投稿数が0件のタームが大量に溜まっていたので、それを一気に削除したいと言われました。

しかし管理画面から一個一個タームをチェックで選択していては、多大な時間がかかってしまいます。

そこで、投稿数が0のタームのIDだけをget_termsで一気に抽出して削除処理を行おうと思ったのですが、その方法がわからず…。

広告

投稿数0件も含めて抽出ならできるけど…。

$terms = get_terms("hogehoge", [
	"hide_empty" => false,
	"fields" => "ids",
]);
foreach($terms as $term_id) {
	wp_delete_term($term_id, "hogehoge");
}

通常だと、get_termsをしても投稿数が0件のタームを抽出することはできません。

でも上記のように、get_termsの引数にhide_empty falseを入れることで、投稿数0件のタームも抽出の対象にすることができます。

しかし、今回やりたいのは、投稿数0件のタームも含めて抽出ではなく、0件のタームだけを抽出すること。

件数に関する条件を引数で指定できないのかと思ったのですが、そのような引数は用意されていませんでした。

抽出条件は自作できる。

get_termsは実質、データベースに対してSQL文を実行し、条件どおりのタームを抽出してくる処理です。

そして、get_termsの中にはterms_clausesというフックが用意されており、SQL文を書き換えることができるのです。

$terms = get_terms("hogehoge", [
	"hide_empty" => false,
	"empty" => true,
	"fields" => "ids",
]);
foreach($terms as $term_id) {
	wp_delete_term($term_id, "hogehoge");
}

新しい引数を追加することもできるので、今回はemptyという引数がtrueになっていることで、投稿数0のタームのみ抽出できるようにします。

emptyという引数は、もともとget_termsには存在しないものです。

add_filter("terms_clauses", function ($clauses, $taxonomy, $args) {
	if(isset($args["empty"]) && $args["empty"] === true) {
		$clauses["where"] .= " AND tt.count = 0";
	}
	return $clauses;
}, 10, 3);

terms_clausesの3つの引数のうち、左から順に今回実行される予定のSQL文を分割したもの、get_termsで指定したタクソノミー、get_termsで指定した引数が入ります。

上のコードの場合、$argsの配列の中にemptyがあるので、それをif文で比較して、trueだったらSQL文の書き換えを行います。

なお、issetも入れている理由ですが、もし引数にemptyを指定しなかった場合、存在しない変数を参照することになり、noticeが発生してしまうので、仕方なく入れています。

そしてSQL文のwhere句の中に、投稿数が0のものだけということを表す条件を追記することで、投稿数が0のタームのみ抽出するget_termsが完成します。

SQL文の記述方法について、多少の理解がないと実現できないのが残念なところですが、とにかくこれで0件のタームを一気に削除することができました。

しかし、もし削除対象のタームが何百件も存在する場合、削除の途中でメモリリークを起こす恐れがあります。

面倒ですがjavascriptも利用してajax処理を作って、画面上に進捗を表示しつつターム削除を行う方がいいかもしれません。

そのためのコードですが、私自身まだ作れていないので、今回は省略します。