java-beginner.com ブログ

プログラミングを学習するブログ(Javaをメインに)

全ての記事の一覧をカテゴリー階層で出力する関数を自作した

投稿日:

最終更新日:2021年08月12日

アイキャッチ

こんにちは。今回はWordPressについて記事です。最近、ブログの記事の一覧を出力する機能を自作しようと思いました。カテゴリー階層とともに出力する処理を自作したので、そのプログラムを紹介します。

動機

以前、固定ページに記事(投稿ページ)の一覧をプラグインを使って表示していました。しかし、そのプラグインの最終更新日を調べた結果、古いまま更新が止まっていることがわかりました。WordPressのバージョンはどんどん新しくなるため、そのプラグインを使い続けるか迷っていました。

その後、記事一覧を出力する機能を持つWordPressテーマを使いました。しばらくの間、そのテーマを使っていたのですが、他のテーマを使うことにしました。

現在(この記事を書いている時点)、使っているテーマには記事一覧を出力する機能はありません。そのため、一覧出力の処理を自作しようと考えました。

以前に使っていたプラグインは以下のような表示をしてくれました。

  • カテゴリーの階層を表示。
  • カテゴリーそれぞれに対して、属する記事をすべて表示。

身近な例で例えるとフォルダーとファイルのツリー表示のようなものです。

上記のような出力処理を自作しようと、WordPressに用意されている関数を調べました。そして、特定の仮定の下で一覧を出力するプログラムを作りました。今回の記事はそのプログラムを自作したときの備忘録です。

テスト時の状況

ローカルにテスト用のWordPress環境を作りました。

テスト用のカテゴリーと投稿ページをいくつか作りました。

作成した記事は5つです。記事のタイトルを以下のように設定しました。

  • post0001
  • post0005

カテゴリーの階層と所属する投稿の数を以下のようにしました。

カテゴリー階層 記事数
cat-a 0
— cat-a-01 1
— cat-a-02 2
cat-b 0
— cat-b-01 0
— — cat-b-01-01 1
cat-c 1

上記のように、最下層のカテゴリーだけに記事が所属している想定です。

以下のようにそれぞれの記事をカテゴリーに振り分けました。

親カテゴリー 子カテゴリー 孫カテゴリー 記事
cat-a cat-a-01 post0001
cat-a-02 post0002
post0003
cat-b cat-b-01 cat-b-01-01 post0004
cat-c post0005

プログラムの出力結果の想定は以下のような階層別の表示です。

カテゴリーA
 カテゴリーA-01
  記事タイトル0001
 カテゴリーA-02
  記事タイトル0002
  記事タイトル0003
カテゴリーB
 カテゴリーB-01
  カテゴリーB-01-01
   記事タイトル0004
カテゴリーC
 記事タイトル0005

上記のように、カテゴリーの階層を表示して、所属する記事の一覧を出力するのが目的です。

関数get_categoriesで階層を辿る

目的を達成するためにWordPressに用意された関数を調べた結果、関数get_categories()を使うことにしました。

上記関数は配列を引数に指定することができます。配列に設定するキーはいくつか用意されていますが、今回は以下のキーを使用しました。

キー 内容
parent 親のカテゴリーID

上記をキーに持つ配列が引数に設定された場合、IDが「parent」の値と同じであるカテゴリーの子カテゴリーだけが返却されます。特に、キー「parent」の値が「0」である場合、この関数は一番上位のカテゴリー(それ以上親を持たないカテゴリー)を取得します。

この関数はカテゴリーのオブジェクトを配列で返却します。

以下は「parent」の値に「0」を設定した例です。

ソース

function my_function() {
    $args = array(
        'parent' => 0,
    );

    $categories = get_categories( $args );

    echo '<pre>';
    print_r($categories);
    echo '</pre>';
}

結果(2番目以降の要素は省略)

Array
(
    [0] => WP_Term Object
        (
            [term_id] => 1
            [name] => cat-a
            [slug] => cat-a
            [term_group] => 0
            [term_taxonomy_id] => 1
            [taxonomy] => category
            [description] => 
            [parent] => 0
            [count] => 0
            [filter] => raw
            [term_order] => 0
            [cat_ID] => 1
            [category_count] => 0
            [category_description] => 
            [cat_name] => cat-a
            [category_nicename] => cat-a
            [category_parent] => 0
        )
    ...
)

上記のget_categories関数が返却した配列には最上位のカテゴリーの情報が格納されています。

今回の場合、最上位のカテゴリーである「カテゴリーA」、「カテゴリーB」、「カテゴリーC」のオブジェクトが格納されています。キー「cat_ID」に設定されている値は、そのカテゴリーのIDです。この値を使って配列を作り、再び関数get_categories()を呼び出すことで、そのカテゴリーの子カテゴリーを辿ることができます。

例えば、以下のよう再帰呼び出しを利用することができます。

ソース

function my_shortcode_function() {
    my_function(0);
}

// 再帰呼び出しの関数
function my_function( $cat_ID ) {
    $args = array(
        'parent' => $cat_ID,
    );

    $categories = get_categories( $args );

    echo '<ul>';
    if ( empty( $categories ) ) {
        // 子カテゴリーが存在しない場合
        echo '<li>ID:' . $cat_ID . '</li>';
    } else {
        // それ以外の場合
        foreach ( $categories as $category ) {
            echo '<li>' . $category->name;
            my_function( $category->cat_ID );
            echo '</li>';
        }
    }
    echo '</ul>';
}

出力されたHTML(整形してます)

<ul>
  <li>cat-a<ul>
      <li>cat-a-01<ul>
          <li>ID:4</li>
        </ul>
      </li>
      <li>cat-a-02<ul>
          <li>ID:5</li>
        </ul>
      </li>
    </ul>
  </li>
  <li>cat-b<ul>
      <li>cat-b-01<ul>
          <li>cat-b-01-01<ul>
              <li>ID:7</li>
            </ul>
          </li>
        </ul>
      </li>
    </ul>
  </li>
  <li>cat-c<ul>
      <li>ID:3</li>
    </ul>
  </li>
</ul>
0001

上記のmy_function関数は以下の分岐処理をします。

  • 引数$cat_IDに対応するカテゴリーについて、その子カテゴリーが存在しない場合、$cat_IDを出力する。
  • それ以外の場合、子カテゴリーの分だけ、my_function()を呼び出す。

1番目の処理に進む場合は、子カテゴリーが存在しない場合です。その場合、引数$cat_IDに対応するカテゴリーは末端のカテゴリーということになります。

2番目の処理に進む場合は、子カテゴリーが存在する場合です。この場合、自分自身の再帰呼び出しをします。例えば、ループ処理の一回目では$categoryに「カテゴリーA」のオブジェクトが格納されています。カテゴリー名を出力した後、「カテゴリーA」のIDで関数my_functionを呼び出します。呼び出し前後にli要素の開始タグ、終了タグを出力しているため、li要素の内部にul要素が出力されます。

1番目の部分の処理内容を、記事一覧にすれば、目的のプログラムが完成です。

なお、最初にショートコードの定義があるのは、固定ページにショートコードを記述して使うためです。

完成したプログラム

上記プログラムに記事一覧の出力処理を追加しました。記事一覧の出力の個所は、WP_Queryを使いました。この部分は一般的な方法を使っています。

以下が完成したプログラムです。

ソース

<?php
add_shortcode( 'my_shortcode', 'my_shortcode_function');

function my_shortcode_function() {
    my_function(0);
}

// 再帰呼び出しの関数
function my_function( $cat_ID ) {
    $args = array(
        'parent' => $cat_ID,
    );

    $categories = get_categories( $args );

    echo '<ul>';
    if ( empty( $categories ) ) {
        // 子カテゴリーが存在しない場合
        print_posts( $cat_ID );

    } else {
        // それ以外の場合
        foreach ( $categories as $category ) {
            echo '<li>' . $category->name;
            my_function( $category->cat_ID );
            echo '</li>';
        }
    }
    echo '</ul>';
}

// カテゴリーに属する記事情報をli要素で出力
function print_posts( $cat_ID ) {
    $args = array(
        'posts_per_page' => -1, // (*1)
        'orderby' => 'date',
        'order'   => 'ASC',
        'cat'     => $cat_ID,
    );

    $query = new WP_Query( $args );
	if ( $query->have_posts() ) {
		while ( $query->have_posts() ) {
			$query->the_post();
            ?><li><a href="<?php the_permalink(); ?>"><?php the_title(); ?></a></li><?php
		}
	}
    wp_reset_postdata();
}

(*1)は20220320に追加。

出力されたHTML(整形してます)

<ul>
  <li>cat-a<ul>
      <li>cat-a-01<ul>
          <li><a href="[home url]/post0001/">post0001</a></li>
        </ul>
      </li>
      <li>cat-a-02<ul>
          <li><a href="[home url]/post0002/">post0002</a></li>
          <li><a href="[home url]/post0003/">post0003</a></li>
        </ul>
      </li>
    </ul>
  </li>
  <li>cat-b<ul>
      <li>cat-b-01<ul>
          <li>cat-b-01-01<ul>
              <li><a href="[home url]/post0004/">post0004</a></li>
            </ul>
          </li>
        </ul>
      </li>
    </ul>
  </li>
  <li>cat-c<ul>
      <li><a href="[home url]/post0005/">post0005</a></li>
    </ul>
  </li>
</ul>
0002

上記プログラムでは自作関数print_posts()を新しく定義しました。この関数では、記事のタイトルをリンクで出力します。

カテゴリーに所属する記事を取得するため、WP_Query関数を使っています。関数print_posts()の仮引数$cat_IDをWP_Query関数の引数に指定することで、そのカテゴリーに所属する記事が取得できます。後は返却された配列の情報をwhile文を使い、出力しています。この繰り返し処理は記事の一覧を出力する標準的な処理です。

子カテゴリーが存在しない場合に自作関数print_posts()が自作関数my_function()から呼び出されます。今回、用意した記事は最下層のカテゴリーにのみ所属しています。そのカテゴリーに対して、自作関数my_function()が呼び出されます。そのため、ちょうど階層表示のように記事一覧が出力されます。

以上、参考になれば幸いです。