ワードプレスのタグクラウドをカスタマイズ(上級者向け)

2018年3月21日

タグクラウドのカスタマイズ方法に関する記事はネットにたくさんありますが、ワードプレスが内部で実行している wp_tag_cloud 関数に指定する引数を変更することでデフォルトの動作を変更するという内容が多いようです。

引数を変えるだけで望み通りのカスタマイズが実現できる場合が多いからでしょうね。

ウィジェット以外の場所に設置するタグクラウドの挙動を変えたい場合

ウィジェット以外の場所にタグクラウドを設置したい場合は、
テンプレートファイルの該当箇所に次のコードを記述すればOKです。

<?php wp_tag_cloud(); ?>

こうすると、ウィジェットで追加できる「タグクラウド」と同じ動作をします。

wp_tag_cloud 関数は引数を省略するとウィジェットのタグクラウドと同じ動作をするからです。
たとえば、表示順は「タグの名前順」、最大表示件数は「45」といった具合です。

正確にいうと、ウィジェットのタグクラウドも内部的に wp_tag_cloud 関数を使っており、その際に使われる引数はほとんどデフォルトの設定値です。

wp_tag_cloud 関数に指定できる引数には、

  1. タグの名前順ではなく記事の件数が多い順に並び替えたい
  2. ページを開くたびにランダムに順番を変えたい
  3. タグの文字サイズを大きくしたい/小さくしたい
  4. 特定のタグだけタグクラウドから除去したい

など、私たちがよく使うであろう設定が用意されています。

たとえば記事の件数が多い順に表示させるには次のようにします。

<?php wp_tag_cloud('orderby=count&order=DESC'); ?>

または、引数が多いときは次のように引数をあらかじめ配列変数に代入してから関数に引き渡します。

<?php
$args = array(
    'orderby' => 'count',  //記事件数で並び替える
    'order'   => 'DESC',   //大きい順
);
wp_tag_cloud($args); 
?>

引数の詳細はWordPressの公式マニュアル(CODEX)で確認できます。

wp_tag_cloud 関数

ウィジェットのタグクラウドの挙動を変えたい場合

一方、何もプログラムを書かなくても利用できる「外観 > ウィジェット」にあるタグクラウドを使う場合、タグクラウドの実行はワードプレスの内部で行われるので、wp_tag_cloud 関数を直接的にカスタマイズすることはできません。

このような場合は、フィルターフックという仕組みを使って、ワードプレスの内部で実行される処理の一部分を自分で作成したPHP関数に置き換えるというアプローチを使います。

フィルターフックとは?

ここからはPHPのプログラムが読み書きできる経験者向けのお話です。

たとえば次のような自作関数を functions.php に追加すると、ウィジェットのタグクラウドだろうとテンプレートファイルに直接 wp_tag_cloud 関数を記述した場合だろうと、要するに wp_tag_cloud 関数が内部的に実行される全てのタイミングで、wp_tag_cloud 関数の挙動が変わります。

// タグクラウド実行関数に与えるパラメータを変更
function custom_widget_tag_cloud_args($args) {
	$myargs = array(
		'orderby' => 'count',  // タグ名ではなく記事件数をソートキーとする
		'order' => 'DESC',     // ソート値が大きい順(記事件数が多い順)にソートする
	);
	$args = wp_parse_args($args, $myargs);
	return $args;
}
add_filter('widget_tag_cloud_args', 'custom_widget_tag_cloud_args');

add_filter 関数は、「1番目の引数が示すタイミングで2番目の引数が示す関数を実行してください」とワードプレスに予約する関数です。

この仕組みがあるおかげで、私たちはワードプレスの内部にあるプログラムを直接書き換えることなく、好きなようにプログラムの挙動を変えることができるのです。

widget_tag_cloud_args フィルターは、その名前が示すとおり、ウィジェットのタグクラウドを生成する処理に与えるcode>args(argument:引数)を変更するために用意されたフックポイントです。

ですから、このフィルターにフックさせる自作関数では、タグクラウドの生成に使われる引数が詰め込まれた配列を受け取り、その配列の中身を自由に変更した結果を return で戻してあげればよいのです。

この例では、タグクラウドのデフォルトの挙動である「タグの名前の昇順」を「記事件数の降順」に変更しています。

フィルターフックを使ったより複雑なカスタマイズ

こんな場合はどうでしょうか?

「記事が1件しかないタグはユーザーに見せたくないので、タグクラウドから消したい」

残念ながら、

記事件数が何件以下のタグは表示しないという指定ができる引数は wp_tag_cloud 関数にありません。

じゃあどうするかというと、これもフィルターフックで実現できます。

ワードプレスのインストールディレクトリにある wp-includes / category-template.php を開くと、wp_tag_cloud 関数のプログラムロジックを直接確認することができます。

これがwp_tag_cloud 関数の定義です。

function wp_tag_cloud( $args = '' ) {
	$defaults = array(
		'smallest' => 8, 'largest' => 22, 'unit' => 'pt', 'number' => 45,
		'format' => 'flat', 'separator' => "\n", 'orderby' => 'name', 'order' => 'ASC',
		'exclude' => '', 'include' => '', 'link' => 'view', 'taxonomy' => 'post_tag', 'post_type' => '', 'echo' => true,
		'show_count' => 0,
	);
	$args = wp_parse_args( $args, $defaults );

	$tags = get_terms( $args['taxonomy'], array_merge( $args, array( 'orderby' => 'count', 'order' => 'DESC' ) ) ); // Always query top tags

	if ( empty( $tags ) || is_wp_error( $tags ) )
		return;

	foreach ( $tags as $key => $tag ) {
		if ( 'edit' == $args['link'] )
			$link = get_edit_term_link( $tag->term_id, $tag->taxonomy, $args['post_type'] );
		else
			$link = get_term_link( intval($tag->term_id), $tag->taxonomy );
		if ( is_wp_error( $link ) )
			return;

		$tags[ $key ]->link = $link;
		$tags[ $key ]->id = $tag->term_id;
	}

	$return = wp_generate_tag_cloud( $tags, $args ); // Here's where those top tags get sorted according to $args

	/**
	 * Filters the tag cloud output.
	 *
	 * @since 2.3.0
	 *
	 * @param string $return HTML output of the tag cloud.
	 * @param array  $args   An array of tag cloud arguments.
	 */
	$return = apply_filters( 'wp_tag_cloud', $return, $args );

	if ( 'array' == $args['format'] || empty($args['echo']) )
		return $return;

	echo $return;
}

10行目の get_terms 関数でタグクラウドに表示するタグをデータベースから取得し、
27行目の wp_generate_tag_cloud 関数で表示用のHTMLを作成しています。

HTMLの作成が終わってからでは遅いので、wp_generate_tag_cloud 関数の途中にプログラムを割り込ませることができないか? と考えます。

そのような期待をしながら、wp_generate_tag_cloud 関数の定義を見て、フィルターフックができるポイントがないかどうかを探します。

function wp_generate_tag_cloud( $tags, $args = '' ) {
	$defaults = array(
		'smallest' => 8, 'largest' => 22, 'unit' => 'pt', 'number' => 0,
		'format' => 'flat', 'separator' => "\n", 'orderby' => 'name', 'order' => 'ASC',
		'topic_count_text' => null, 'topic_count_text_callback' => null,
		'topic_count_scale_callback' => 'default_topic_count_scale', 'filter' => 1,
		'show_count' => 0,
	);
~~~中略~~~
	$tags_sorted = apply_filters( 'tag_cloud_sort', $tags, $args );
~~~中略~~~
	$tags_data = apply_filters( 'wp_generate_tag_cloud_data', $tags_data );
~~~中略~~~
	if ( $args['filter'] ) {
		return apply_filters( 'wp_generate_tag_cloud', $return, $tags, $args );
	}
	else
		return $return;
}

期待通り、ありましたね。

3箇所に存在する apply_filters がフィルターフックできるポイントです。

次に、どのフックポイントを使うべきかを考えます。

1つ目の tag_cloud_sort は、名前から想像すると、タグの並び順をカスタマイズするためのフィルターです。今の目的は並び順を変更することではありませんから、ここは無視します。

3つ目の wp_generate_tag_cloud は一番最後のフィルターポイントなので、ここでよさそうに思えますが、引数を見るとここではないことが判断できます。$return にはブラウザに表示する直前の整形済みのHTMLが入っています。$tags には表示の元となるタグの配列、$args にはデータベースからタグを取得した際に使われた最終的な検索条件の配列が入っています。つまり、$tags や $args を元にして、$return の代わりになるHTMLの整形処理を自分で行いたい場合に使う奥の手のようなフックポイントだということが想像できます。そこまで踏み込むととても大変ですから、ここも無視します。

すると残る候補は12行目の wp_generate_tag_cloud_data です。ここにフックをしかけます。

テーマの functions.php に次のフック関数を記述します。

function custom_wp_generate_tag_cloud_data($tags_data) {
    foreach ( $tags_data as $key => $value ) {
        if ($tags_data[$key]['real_count'] <= 1) {
            unset($tags_data[$key]);
        }
    }
    return $tags_data;
}
add_filter('wp_generate_tag_cloud_data', 'custom_wp_generate_tag_cloud_data');

apply_filters 関数の箇所が自作関数 custom_wp_generate_tag_cloud_data に置き換わって実行されるというイメージでよいでしょう。

apply_filters 関数に渡される引数 $tags_data を受け取る自作関数内では、$tags_data に入っているタグの配列のうち、投稿件数が1以下のものを unset() で除去し、表示したいタグだけを残した配列を return で返しています。

ワードプレスの内部で行われるタグクラウドのHTML生成処理の途中に間接的に割り込んで、処理のロジックを変更しているわけですね。

フィルターフックを使いこなすには、

  1. どのようなフィルターが用意されているのか?
  2. フィルター関数の引数には何が渡されるのか?
  3. フィルター関数の戻り値は何を返せばよいのか?

など、プログラムを解読する力が必要ですが、使えるようになると格段にカスタマイズの幅が広がります。ぜひ挑戦してみてください。