WordPress でブログを運用していると、カテゴリーやタグ、カスタムタクソノミーを整理したくなることがあります。
特にタグのように非階層型の入力形式のタクソノミーの場合は自由入力なので、スペースの有無や誤字等で似たようなタームを誤って生成しがちです。
Windows 11
と Windows11
(スペースの有無)Google Chrome
と Gooogle Chrome
(誤字。 o
が1つ多い)しかも、異なるタグとして集計されるのでタグクラウドの中から絞り込むと思わぬところで必要な記事が漏れていて「あの記事絶対タグが付いているはずなのに……」と見付からずに右往左往したり。
基本的には管理画面の「投稿」→「タグ」に進んでタグの一覧からどちらか一方にタグを付け直して、もう片方のタグで「カウント」つまり記事数が0になったタグを削除すれば良いとは思います。
しかし、ここで一つ課題が。
技術記事等で自分用メモとして残すための非公開記事がいくつかあったりするのですが、先程のタグ編集画面の「カウント」は非公開記事は除外された数のようです。
そのため、例えタグ編集画面で「カウント」が0になっていても、実際は非公開記事で使用されていた……ということが考えられます。これでは迂闊にタグを削除することができません。
そこで、タグ編集画面で非公開記事も含めたカウントを表示するプラグインを作りました。
class FrostColumns
{
protected $taxName;
protected $postTypeName;
protected $countIncludePrivateID;
protected $countIncludePrivateLabel;
/**
* mb_strlen() での空文字列判定のラッパー関数
*
* @param string $str : 入力文字列
*
* @return boolean : 出力文字列
*/
public function boolStrLen( $str )
{
return mb_strlen(
$str,
'UTF-8'
) > 0;
}
/**
* コンストラクタ
*
*/
public function __construct()
{
if(
mb_strpos(
parse_url(
$_SERVER['REQUEST_URI'],
PHP_URL_PATH
),
'edit-tags.php',
0,
'UTF-8'
) !== false
) {
// edit-tags.php のみで動作
$tax = esc_html(
filter_input(
INPUT_GET,
'taxonomy',
FILTER_SANITIZE_SPECIAL_CHARS
)
);
// タクソノミーの名前を取得
$this->taxName = $this->boolStrLen( $tax )
? $tax
: '';
$posttype = esc_html(
filter_input(
INPUT_GET,
'post_type',
FILTER_SANITIZE_SPECIAL_CHARS
)
);
// 投稿タイプの名前を取得
$this->postTypeName = $this->boolStrLen( $posttype )
? $posttype
: '';
// ラベル
$this->countIncludePrivateID = 'count_include_private';
$this->countIncludePrivateLabel = 'カウント(非公開・予約含)';
}
}
/**
* SQLを直接発行して公開済み・非公開・予約投稿の記事の投稿IDのみの一覧を取得、その数をリンクと共に出力する
*
* @param int $termID : タームID
*
*/
public function countArticlesBySQL( $termID )
{
// $wpdb 読み込み (global で宣言した変数のスコープの兼ね合いでここで読み込み宣言する)
require_once( ABSPATH . '/wp-load.php');
global $wpdb;
// 各タームのオブジェクトを取得
$termObj = get_term( $termID, $this->taxName );
// 投稿のカテゴリー、タグはキー名がタクソノミー編集ページと投稿一覧ページで異なるので変換する
$taxNameParam = $this->taxName;
switch ( $this->taxName ) {
case 'category':
$taxNameParam = 'category_name';
break;
case 'post_tag':
$taxNameParam = 'tag';
break;
default:
$taxNameParam = $this->taxName;
break;
}
// カスタム投稿タイプならばリンクのGETパラメータに投稿タイプの名前を付与
$postTypeParam = $this->boolStrLen( $this->postTypeName )
? '&post_type=' . $this->postTypeName
: '';
// SQLのクエリで指定する投稿タイプの名前の文字列をセット
$postTypeDBParam = $this->boolStrLen( $this->postTypeName )
? $this->postTypeName
: 'post';
// WP_Query では結果の投稿オブジェクトが大き過ぎてメモリを食い潰すので直接SQLを発行してIDのみ結果として取得して省力化する
$the_query = "SELECT " . $wpdb->prefix . "posts.ID FROM " . $wpdb->prefix . "posts LEFT JOIN " . $wpdb->prefix . "term_relationships ON (" . $wpdb->prefix . "posts.ID = " . $wpdb->prefix . "term_relationships.object_id) WHERE 1=1 AND ( " . $wpdb->prefix . "term_relationships.term_taxonomy_id IN ( %d ) ) AND " . $wpdb->prefix . "posts.post_type = %s AND ((" . $wpdb->prefix . "posts.post_status = 'publish' OR " . $wpdb->prefix . "posts.post_status = 'future' OR " . $wpdb->prefix . "posts.post_status = 'private')) GROUP BY " . $wpdb->prefix . "posts.ID ORDER BY " . $wpdb->prefix . "posts.post_date DESC";
$results = $wpdb->get_results(
$wpdb->prepare(
$the_query,
$termID,
$postTypeDBParam
)
);
$cnt = count($results);
echo <<<CNT
<a href="edit.php?{$taxNameParam}={$termObj->slug}{$postTypeParam}">{$cnt}</a>
CNT;
}
/**
* 欄としての列の出力
*
* @param array $columns : 列
*
* @return array $columns : 列
*
*/
function addCountIncludePrivateColumns( $columns )
{
echo <<<STL
<style>
.taxonomy-{$this->taxName} .manage-column.num \{width: 90px;\}
.taxonomy-{$this->taxName} .manage-column.column-id \{width: 60px;\}
</style>
STL;
$columns[$this->countIncludePrivateID] = $this->countIncludePrivateLabel;
return $columns;
}
/**
* 追加した列に実際の値を表示させる
*
* @param string $content : 出力するコンテンツ
* @param string $column_name : 列名
* @param int $term_id : タームID
*
*/
function customCountIncludePrivateColumns( $content, $column_name, $term_id )
{
if ( $column_name == $this->countIncludePrivateID ) {
$this->countArticlesBySQL( $term_id );
}
}
/**
* 初期処理。アクションフック・フィルターフックを発動させる
*
*/
public function initialize()
{
// 列の追加
add_filter(
'manage_edit-' . $this->taxName . '_columns',
[
$this,
'addCountIncludePrivateColumns'
]
);
// 追加した列に実際の値を表示させる
add_action(
'manage_' . $this->taxName . '_custom_column',
[
$this,
'customCountIncludePrivateColumns'
],
10,
3
);
}
}
// 処理
$wp_ab_frostcolumns = new FrostColumns();
if( is_admin() ) {
// 管理者画面を表示している場合のみ実行
$wp_ab_frostcolumns->initialize();
}
edit-tags.php
のみで動作するように判定countArticlesBySQL
メソッド:
wp-load.php
から $wpdb
で WordPress のDBオブジェクトを読み込みedit-tags.php
ではカテゴリーは category
、タグは post_tag
だが投稿一覧ページではそれぞれ category_name
, tag
とキーとなる文字列が変わるため遷移時の GETパラメータ に付与するときのために変換をかけておくpublish
, private
, future
のいずれか)で絞り込むSQLクエリを発行して投稿IDのみのオブジェクトを取得する
count
で取得して、 a
タグ として出力addCountIncludePrivateColumns
メソッド:
customCountIncludePrivateColumns
メソッド:
countArticlesBySQL
メソッド を呼ぶやっていることとしてはこのような感じ。
それでは、デモで検証してみます。
このように9つの記事を適当に作ってみました。今回はプラグインの効果をタグで試すことにします。
例えば、「ヘイルストーム」で記事を検索すると公開記事2件、非公開記事2件と2件ずつ、合計4件がヒットします。
ところが、通常のタグの編集画面では先の「ヘイルストーム」の記事数は2と表示されています。実際は非公開記事も含めて4件あるはずで、今回は「4」と表示されてほしいわけです。
それじゃスペルカード発動。先の「ヘイルストーム」の記事数は追加された列で4と表示されるようになりました。意図通りです。
別のサンプル。誤字した例として伸ばし棒のない「コールドディヴィニティ」は上述のタグ編集画面だと通常0件、追加列では1件と表示されています。ページ遷移すると、非公開記事1件のみが一覧に表示されています。
こういうケースでは伸ばし棒のある「コールドディヴィニティー」に直して、プラグインで追加された列も含めて「0」表示となったことを確認した後に伸ばし棒のない「コールドディヴィニティ」タグを削除する、というフローで活用できる、という寸法です。
$wp_list_table->display();
でテーブルを出力している。
基底クラスのこの部分が該当処理。ただし汎用的な内容。
使用しているクラス
column_posts()
メソッドがそれっぽい。が、引数の $tag
に既に情報が入っているようなのでこの中ではない。
display_rows()
メソッドでもない。
found_posts
プロパティ
複雑な条件の件数となると WP_Query
一択。
count
プロパティ はDBの値を読むだけ。逆に言えばDBに値が保存されていることが判明。
get_tax_sql()
filter_input(INPUT_GET, 'taxonomy', FILTER_SANITIZE_SPECIAL_CHARS)
で取得できる。
Crieitは誰でも投稿できるサービスです。 是非記事の投稿をお願いします。どんな軽い内容でも投稿できます。
また、「こんな記事が読みたいけど見つからない!」という方は是非記事投稿リクエストボードへ!
こじんまりと作業ログやメモ、進捗を書き残しておきたい方はボード機能をご利用ください。
ボードとは?
コメント