【WordPress】カスタムタクソノミーの初期タームと並び順をプラグインなしでコード管理する
WordPress でカスタムタクソノミーを作ったとき、「最初から用意しておきたいカテゴリー」や「カテゴリーの並び順」をどう管理するかで迷う人もいると思います。この記事では、プラグインを使わず、テーマのコードだけで初期タームの登録と並び順を管理する方法を紹介します。
- 初期タームと並び順を、プラグインなしでコード管理するメリット
- 設定の配列から、タクソノミーと初期タームをまとめて登録する方法
- 設定が変わったときだけ登録を走らせて、DB への問い合わせを減らす工夫
- タームの並び順を、設定に書いた順にそろえる方法
なぜプラグインを使わずにコードで管理するのか
オレオレナリでは、WordPress サイトを作るときに「プラグインを入れなくても実装できるものは、プラグインを使わずに実装する」という方針にしています。
依存するプラグインは、少ないほどセキュリティ的にも運用的にも良い。テーマのコードで無理なく書けるものは、テーマで書く。
タームの並び替えや初期データの投入は、プラグインを入れれば管理画面からできます。ただ、この程度の処理ならテーマの PHP で十分に書けます。コードで管理すると、こんなメリットがあります。
| 観点 | コードで管理するメリット |
|---|---|
| セキュリティ | プラグインが減るぶん、脆弱性が見つかったときに影響を受ける入り口が減る |
| 運用 | プラグインの更新作業や、WordPress 本体の更新に合わせた相性の確認が減る |
| 環境差 | ローカル・本番で同じコードが動くので、「本番でカテゴリーを作り忘れた」「スラッグを打ち間違えた」が起きない |
| 履歴 | Git で管理できるので、いつ・なぜカテゴリーを変えたかが残る |
もちろん、サイトを運用する人がコードを触らず、管理画面で自由にカテゴリーを増やしたり並べ替えたりしたい場合は、管理画面やプラグインで管理するほうが向いています。どちらが良いかは、誰がサイトを触るかで決めるのがおすすめです。
設定はひとつの配列にまとめる
まず、タクソノミーの設定を配列にまとめて返す関数を用意します。タクソノミーを増やしたいときは、ここに足すだけで済むようにしておきます。
function ore2nari_taxonomies(): array {
return array(
// ブログ
array(
'slug' => ORE_TAXONOMY_SLUG_BLOG_CAT,
'label' => 'カテゴリー',
'post_types' => array( ORE_POST_TYPE_SLUG_BLOG ),
'hierarchical' => true,
'terms' => array(
array( 'slug' => 'dev', 'name' => '開発' ),
array( 'slug' => 'making', 'name' => '制作の裏側' ),
array( 'slug' => 'tips', 'name' => '制作のヒント' ),
array( 'slug' => 'talk', 'name' => '雑談' ),
),
),
// …ほかのタクソノミーも同じ形で並べる
);
}
それぞれのキーの意味はこんな感じです。
- slug
-
タクソノミーのスラッグ。URL にも使います。
- label
-
管理画面での名前。
- post_types
-
紐付ける投稿タイプ。
- hierarchical
-
true ならカテゴリー型、false ならタグ型。
- terms
-
初期ターム。ここに書いた順が、そのまま並び順になります。
タクソノミーを登録する
設定の配列をループして、register_taxonomy() を呼びます。管理画面の文言(ラベル)を作る関数は、長くなるので省略しています。
add_action( 'init', function () {
foreach ( ore2nari_taxonomies() as $taxonomy ) {
$args = array(
'labels' => ore2nari_taxonomy_labels( $taxonomy['label'], $taxonomy['hierarchical'] ),
'hierarchical' => $taxonomy['hierarchical'],
'public' => true,
'show_in_rest' => true, // ブロックエディターで選べるようにする
'show_admin_column' => true, // 投稿一覧に列を出す
'rewrite' => array(
'slug' => $taxonomy['slug'],
'with_front' => false,
'hierarchical' => $taxonomy['hierarchical'],
),
);
register_taxonomy( $taxonomy['slug'], $taxonomy['post_types'], array_merge( $args, $taxonomy['args'] ?? array() ) );
}
} );
show_in_rest を true にしないと、ブロックエディターのサイドバーにタクソノミーが出てこないので注意です。
初期タームを登録する
ここがこの記事の本題です。設定の terms に書いたタームを、まだなければ登録します。
ポイントは、設定が変わったときだけ登録処理を走らせることです。毎回のページ表示で「このタームはある?」と DB に問い合わせるのはもったいないので、設定のハッシュを保存しておき、変わったときだけ登録します。
すべてのタクソノミーの terms を、タクソノミーごとの配列にまとめます。slug か name が空の行は除きます。
まとめた配列のハッシュ(md5)を作り、前回保存したハッシュと同じなら、何もせずに終わります。
同じスラッグのタームがなければ wp_insert_term() で登録します。すでにあるタームには触りません。
最後に今回のハッシュをオプションに保存します。次からは設定を変えるまで、登録処理は走りません。
add_action( 'init', function () {
$definitions = array();
foreach ( ore2nari_taxonomies() as $taxonomy ) {
// slug・name が空の行(まだ埋めていない枠)は除く
$terms = array_values( array_filter( $taxonomy['terms'] ?? array(), function ( $term ) {
return '' !== trim( $term['slug'] ?? '' ) && '' !== trim( $term['name'] ?? '' );
} ) );
if ( $terms ) {
$definitions[ $taxonomy['slug'] ] = $terms;
}
}
$hash = md5( wp_json_encode( $definitions ) );
if ( get_option( 'ore2nari_default_terms_hash' ) === $hash ) {
return;
}
foreach ( $definitions as $taxonomy => $terms ) {
foreach ( $terms as $term ) {
if ( ! term_exists( $term['slug'], $taxonomy ) ) {
wp_insert_term( $term['name'], $taxonomy, array( 'slug' => $term['slug'] ) );
}
}
}
update_option( 'ore2nari_default_terms_hash', $hash, true );
}, 20 );
タクソノミーの登録(優先度 10)より後に動くように、優先度を 20 にしています。登録前に wp_insert_term() を呼ぶと、タクソノミーがまだないので失敗します。
この仕組みは「ないものを足す」だけです。設定の terms から行を消しても、すでに登録されたタームは残ります。不要になったタームは、管理画面か WP-CLI で削除してください。
また、管理画面で削除したタームは、設定の terms を変えるまで復活しません(ハッシュが同じなので登録処理が走らないため)。
実際にこのブログでも、カテゴリーを見直して「お知らせ」を設定から外したとき、開発環境にはタームが残ったままになりました。消すときは手で消す、と覚えておくと迷いません。
タームの並び順を設定どおりにする
WordPress のタームは、標準では名前順に並びます。「開発」「制作の裏側」…と決めた順に並べたいので、設定の terms に書いた順に並べ替えるフィルターを付けます。
設定の順番を取り出す
タクソノミーごとに「スラッグ → 何番目か」の配列を作ります。何度も呼ばれるので、static で一度だけ作るようにしています。
function ore2nari_term_order( string $taxonomy ): array {
static $orders = null;
if ( null === $orders ) {
$orders = array();
foreach ( ore2nari_taxonomies() as $setting ) {
if ( ! empty( $setting['terms'] ) ) {
$orders[ $setting['slug'] ] = array_flip( array_column( $setting['terms'], 'slug' ) );
}
}
}
return $orders[ $taxonomy ] ?? array();
}
function ore2nari_sort_terms( array $terms ): array {
// usort は同じ順位のものの順番を変えないので、terms に無いものはもとの順のまま後ろに並ぶ
usort( $terms, function ( $a, $b ) {
$order_a = ore2nari_term_order( $a->taxonomy )[ $a->slug ] ?? PHP_INT_MAX;
$order_b = ore2nari_term_order( $b->taxonomy )[ $b->slug ] ?? PHP_INT_MAX;
return $order_a <=> $order_b;
} );
return $terms;
}
設定にないターム(管理画面で追加したもの)は PHP_INT_MAX 扱いになるので、後ろにまとまります。
PHP 8.0 から、usort などのソートは「同じ順位の要素の順番を変えない」(安定ソート)ようになりました。設定にないターム同士は、もとの名前順のまま後ろに並びます。
get_the_terms と get_terms にフィルターを付ける
記事に付いたタームと、タームの一覧の両方に並べ替えをかけます。
// 投稿に付いたターム(get_the_terms()・get_the_term_list() など)
add_filter( 'get_the_terms', function ( $terms, $post_id, $taxonomy ) {
if ( ! is_array( $terms ) || ! ore2nari_term_order( $taxonomy ) ) {
return $terms;
}
return ore2nari_sort_terms( $terms );
}, 10, 3 );
// タームの一覧(get_terms()。管理画面のチェックボックスなども)
add_filter( 'get_terms', function ( $terms, $taxonomies, $args ) {
// 並べ方を指定しているとき、WP_Term 以外を取るときは何もしない
if ( 'name' !== $args['orderby'] || 'all' !== $args['fields'] ) {
return $terms;
}
// 件数を区切って取るときは、全部が1ページに収まっているときだけ並べ替える
if ( ! empty( $args['offset'] ) || ( ! empty( $args['number'] ) && count( $terms ) >= $args['number'] ) ) {
return $terms;
}
foreach ( (array) $taxonomies as $taxonomy ) {
if ( ore2nari_term_order( $taxonomy ) ) {
return ore2nari_sort_terms( $terms );
}
}
return $terms;
}, 10, 3 );
get_terms のほうは、件数を区切って取っているときに並べ替えないのがポイントです。ページをまたいで取るときに1ページの中だけ並べ替えると、ページ間で順番が崩れてしまいます。
ブロックエディターのチェックボックスはどう取っている?
編集画面のカテゴリーのチェックボックスは、REST API で件数(100件)を区切ってタームを取っています。そのため、タームが100件未満で1ページに収まっているときだけ並べ替える、という条件にしています。カテゴリーが100件を超えることはまずないので、実際にはほぼ並べ替えが効きます。
同じ仕組みはパーマリンクの更新にも使える
「設定のハッシュを保存して、変わったときだけ処理する」という形は、ほかにも使えます。たとえば、カスタム投稿タイプやタクソノミーの定義が変わったときだけ、パーマリンク(リライトルール)を作り直す処理です。
add_action( 'init', function () {
$hash = md5( wp_json_encode( array( ore2nari_post_types(), ore2nari_taxonomies(), ore2nari_use_default_post() ) ) );
if ( get_option( 'ore2nari_rewrite_hash' ) === $hash ) {
return;
}
flush_rewrite_rules( false );
update_option( 'ore2nari_rewrite_hash', $hash, true );
}, 99 );
管理画面の「パーマリンク設定」を保存し直すのと同じことが、本番にデプロイしたときにも自動で行われます。「本番でタクソノミーのページが 404 になる」というよくあるつまずきも防げます。
よくある疑問
変わりません。スラッグで「すでにあるか」を確かめて、あるものには触らないからです。名前を変えたいときは、管理画面で変更してください。
普通に使えます。並び順は、設定に書いたタームの後ろに、名前順で並びます。
並べ替えのプラグインと同時に使うと、どちらの並び順が効くかわかりにくくなります。入れるなら、どちらか一方にするのがおすすめです。
おわりに
初期タームや並び順は、一度作ってしまえば頻繁に変えるものではありません。だからこそ、管理画面の手作業やプラグインに任せるより、コードに書いて Git で管理しておくと安心だなと感じています。設定の配列をひとつ直すだけで、ローカルでも本番でも同じ状態になるのは、地味ですがかなり楽です。
プラグインを入れる前に「これ、テーマで書けないかな?」と一度考えてみると、サイトがすっきりしますよ。
WordPress のカスタマイズのような制作・改修のご相談も受け付けています。お気軽にどうぞ。