[WordPress] 記事のカテゴリーを親子順に並び替え

公開
更新日

スポンサーリンク

WordPressで記事が属するカテゴリーを、get_the_categoryget_the_category_listを使って出力すると、通常は名前順にソートされた状態となります。一つの記事が複数のカテゴリーに属している時、そのカテゴリーの親子関係の順に沿ったリストを得ようとすると、すこし込み入った処理が必要となります。

最近、一つの記事は一つのカテゴリーに必ず属し、そのカテゴリーに親カテゴリーが存在する場合は、それらの先祖カテゴリーすべてにも属するという構成のサイトの開発を行いました。ようするにディレクトリ型のページ構成のサイトということですね。そして、そのサイトでは記事のカテゴリーを親子関係の順に出力する必要がありました。

初めは何も考えずにget_the_category_listを使ってカテゴリーリストの出力を行なっていたのですが、これだと出力されるカテゴリーの一覧は、親子関係を全く反映していないものになるのですね。途中で気がついて、あわてて親子関係を反映するコードを用意しました。以下は、そのコードの作成手順と使い方についてのメモです。

記事がカテゴリー一つと、そのカテゴリーの先祖全てに属すという構成を持つと、カテゴリーを親子順に並べるだけで分かりやすいパンくずリストを作ることができます。仮に、「カテゴリー1」「CAT2」「分類3」「カテゴリー4」「CAT5」というカテゴリーが存在し、「CAT5」が子孫で「カテゴリー1」が先祖という親子関係を持っていたとします。記事Aは、この5つのカテゴリーすべてに属しています。

記事Aを表示する際、get_the_category_listを使うと、カテゴリーを登録した順番によっては、以下の様なでたらめな並びになってしまいます。
WordPressでカテゴリーリストを出力すると、場合によっては並びのおかしなリストとなる

本来は以下のように、親子関係を反映した順序で出力したいわけです。
WordPressでカテゴリーの親子関係を並び順に反映したカテゴリーリスト

さてではどう実現するかですが、あまり知られていないWordPress関数で、cat_is_ancestor_ofという、カテゴリーが他のカテゴリーの先祖カテゴリーであるかどうかを判定する関数がありますので、これを使うことにします。cat_is_ancestor_ofにカテゴリーIDを2つ渡すと、第1引数に渡したカテゴリーIDが、第2引数に渡したカテゴリーIDの先祖であればtrueを、そうでないならfalseを返します。ということは、cat_is_ancestor_offalseを返さなくなるまでソートを行えば、親子順に並んだカテゴリーリストを得られるわけです。まあ単純なバブルソートですね。

前置きが長くなってしまいましたが、それで作ったのが以下のfunctionです。これを、使用中のthemeのfunction.phpにペーストし、WordPressループ内のカテゴリーリストを表示させたい場所でsort_cats_parent_child_relationshipを使えば、親子関係の順に並び替えたカテゴリーリストのhtmlが得られます。

function sort_cats_parent_child_relationship()
{
  //カテゴリー情報を取得
  $cats = get_the_category();
  
  //ソート配列を作成
  foreach($cats as $cat){
    $hash[] = $cat->term_id;
    $catarr[$cat->term_id] = $cat;
  }
  
  //バブルソートで判定(親カテゴリーが上)
  $n=count($hash);
  
  //親子関係でないカテゴリーをソートすると無限ループとなる
  //それが防ぐため、最大計算回数を超えてソートが終わっていない場合は打ち切る
  $worst = $n * ($n-1)/2;//最悪計算時間
  $cnt = 0;
  
  for($i=0;$i<$n;$i++){
    for($j=$n-1;$j>$i;$j--){
      //カテゴリーIDを渡すと、前者が後者の先祖ならtrueを返す
      if( !cat_is_ancestor_of( $hash[$j-1], $hash[$j] ) ){
        $tmp = $hash[$j];
        $hash[$j] = $hash[$j-1];
        $hash[$j-1] = $tmp;
      }
    }
    
    $cnt++;
    //最悪計算時間を超えていないかチェック
    if($cnt>$worst)
      break;
  }

  //順番が確定したので、$hashの並びを元にリストを作成
  //以下で作っているのはtwitter bootstrapのパンくずリスト用のhtml
  foreach($hash as $cat_id)
    $cat_list[] = "<a href='".get_bloginfo("url")."/category/{$catarr[$cat_id]->slug}'>{$catarr[$cat_id]->cat_name}</a>";
  
  $str = "<li>". implode(' <span class="divider">»</span></li><li>', $cat_list). " <span class='divider'>»</span></li>";
  return $str;
}

この関数は、記事に設定されたカテゴリー全てが1対1の親子関係である場合にのみ有効です。親子関係ではない複数のカテゴリーを設定しているような、タグ的な使い方をしている場合には有効ではありません。非常に狭いケースでのみ使用可能な関数となっています。

上記のfunctionでは、使うべきでない場面で使われた場合への対策で、最悪計算回数を超えてもソートが続いている場合は、並び替えを中断するようにしています。(17・18・30・32・33行目)

38行目以下の部分は、twitter bootstrapのパンくずリストのスタイルを適用するためのhtmlを出力しています。実際に使う場合には、この部分を都合のいいように書き変えてお使いください。

<ul class='breadcrumb'>
  <li><a href='<?php bloginfo('url');?>'>Home</a> <span class="divider">»</span></li>
  <?php echo sort_cats_parent_child_relationship();?>
  <li class='active'><?php echo get_the_title();?></li>
</ul>

これで、下のようなカテゴリーが親子順に並べられたリストが出力できます。
WordPressでカテゴリーの親子関係を並び順に反映したカテゴリーリスト

スポンサーリンク


Comment