WordPress:タグクラウドのランダム表示を固定する

WordPress:タグクラウドのランダム表示を固定する

タグクラウドを wp_tag_cloud() で表示する場合、その並び順をランダムにしますと表示される度に変化します。ランダムなんですから当たり前なんですが、それを固定できないかという話です。

01フィルターフック tag_cloud_sort

前記事「WordPress:タグクラウド、タグ一覧表示、タグ別記事一覧」でわかったことは、タグクラウドは wp-includes/category-template.php 内の関数 wp_generate_tag_cloud() で成形されており、並べ替えもその関数内で行われています。category-template.php の892行目からです。

/**
 * Filters how the items in a tag cloud are sorted.
 *
 * @since 2.8.0
 *
 * @param WP_Term[] $tags Ordered array of terms.
 * @param array     $args An array of tag cloud arguments.
 */
$tags_sorted = apply_filters( 'tag_cloud_sort', $tags, $args );
if ( empty( $tags_sorted ) ) {
	return $return;
}

if ( $tags_sorted !== $tags ) {
	$tags = $tags_sorted;
	unset( $tags_sorted );
} else {
	if ( 'RAND' === $args['order'] ) {
		shuffle( $tags );
	} else {
		// SQL cannot save you; this is a second (potentially different) sort on a subset of data.
		if ( 'name' === $args['orderby'] ) {
			uasort( $tags, '_wp_object_name_sort_cb' );
		} else {
			uasort( $tags, '_wp_object_count_sort_cb' );
		}

		if ( 'DESC' === $args['order'] ) {
			$tags = array_reverse( $tags, true );
		}
	}
}

wp_tag_cloud() の パラメータ order に RAND を指定しますと PHP の関数 shuffle を使い、ASC または DESC を指定しますと、まず PHP の関数 uasort を使ってパラメータ orderby の指定に従って name または count で並べ替えた後に、DESC であれば PHP の関数 array_reverse を使って配列を逆順にしています。

ですのでデフォルトではランダムに並べ替えたものを固定することはできませんが、上の 9 行目に tag_cloud_sort というフィルターフックがあります。この戻り値は $tags_sorted に保存された後、元のタグオブジェクトの配列 $tags と比較されて同じでなければ以降の並べ替えルーチンをスルーします。つまり、仮に order や orderby が指定されていたとしても無視されるということです。

このフィルターフック tag_cloud_sort を使えばランダムに並べ替えたものを固定できそうです。

02タームのメタ情報に乱数を登録する

ただ、ランダムに並べ替えるには、それぞれのタグが並べ替えのためのランダム値(乱数)を持っている必要があります。

ここで WordPress のバージョン 4.4 から新たにデータベースに追加されたテーブル wp_termmeta の登場です。この wp_termmeta はタームにメタ情報を追加するためのテーブルです。投稿の postmeta と同じ考えのものがタームにも追加されたということです。登録、更新、取得、削除のために次の関数が用意されています。

これを使えばそれぞれのタグに乱数を登録し、それを使ってタグを並べ替えればランダムであるけれども常に(新規追加で多少変化する…)一定の並び方をしたタグクラウドができるはずです。

通常、メタ情報を、たとえばタグの追加時に個別に登録したいということであればタグ登録画面にカスタムフィールドを追加する必要がありますが、今回は php の rand 関数を使えばいいですので、タグの新規登録の際にバックグラウンドで実行するようにします。

ちなみに、カスタムフィールドを追加する場合は次のアクションフックを使います。

タグであれば、post_tag_add_form_fields, post_tag_edit_form_fields になります。

03すでにあるタグに乱数を登録する

まず、すでにあるタグそれぞれに乱数を登録します。

これは1回だけ実行するものですので、変則的ですが、次の php プログラムをドメイン直下に置いて直接実行します。

<?php
require_once('./wp-load.php');

// 投稿数0のタグにも実行する
$tags = get_tags(['hide_empty'=>false]);
foreach ($tags as $tag){
	// 第4パラメータにtrueで、すでにメタ情報が存在する場合はスルー
	add_term_meta( $tag->term_id, 'random_key' , rand(), true );
}

単独の php プログラムから WordPress の関数を使う場合は冒頭で wp-load.php を読み込みます。

簡単なプログラムです。get_tags() で投稿数 0 のタグも含めたすべてのタグを取得し、それぞれのタグに random_key のキー名で乱数を登録しています。第4パラメータに true を指定していますのでそのタグに random_key のメタ情報が存在していれば次に進みます。

ですのでこのファイルを2度3度実行しても問題はありませんが、余計なものがあってろくなことはありませんので一度実行したら削除しておきます。

実行しますとテーブル wp_termmeta にそれぞれの term_id に紐付けられた random_key のメタ情報が登録されます。

04フィルターフック tag_cloud_sort にソート用コードを書く

functions.php にフィルターフック tag_cloud_sort のコードを書きます。登録した randam_key を使ってソートしています。

function imz_tag_cloud_sort( $tags ){
    uasort( $tags, function( $a, $b ){
        $a_key = get_term_meta($a->term_id, 'random_key');
        $b_key = get_term_meta($b->term_id, 'random_key');
        if( $a_key === $b_key ){
            return 0;
        }
        return $a_key < $b_key ? - 1 : 1;
    });
    return $tags;
}
add_filter( 'tag_cloud_sort', 'imz_tag_cloud_sort' );

php のソート関数 uasort の使い方は php のドキュメントをご覧ください。

これでタグクラウドのランダム表示が固定されました。

残るはタグの新規登録時に random_key のメタ情報を登録するだけです。

05新規タグ登録時にメタ情報を登録する

新規にタグを登録した後に呼び出せるフックはないかと調べますと、wp-includes/taxonomy.php 内に

/**
 * Fires after a new term is created for a specific taxonomy.
 *
 * The dynamic portion of the hook name, `$taxonomy`, refers
 * to the slug of the taxonomy the term was created for.
 *
 * Possible hook names include:
 *
 *  - `create_category`
 *  - `create_post_tag`
	 *
 * @since 2.3.0
 * @since 6.1.0 The `$args` parameter was added.
 *
 * @param int   $term_id Term ID.
 * @param int   $tt_id   Term taxonomy ID.
 * @param array $args    Arguments passed to wp_insert_term().
 */
do_action( "create_{$taxonomy}", $term_id, $tt_id, $args );

というアクションフックがあります。2654行からです。

このアクションフックを create_post_tag として呼び出せば、登録されたタグの $term_id をパラメータとして持っているようですので、これを使えばタグの登録と同時にメタ情報も登録できそうです。

functions.php に次のコードを追加して試してみました。

function imz_create_post_tag( $term_id ){
    add_term_meta( $term_id, 'random_key' , rand(), true );
}
add_action( 'create_post_tag', 'imz_create_post_tag');

うまくいきました! さらにいいことにタグを削除すればメタ情報も削除されます。ですので、この数行で、新規にタグを追加、あるいは削除しても問題なく固定ランダムタグクラウドが維持されます。

完成です! 多分…。しばらくこれで試してみましょう。