渡米生活。(日記)

渡米生活。本家から切り離しました。あまり渡米生活に関係のないプログラムネタや音楽ネタなど。

パーマリンク設定で複数カテゴリーを使う場合に自分が選んだカテゴリを表示させる

wordpressパーマリンク設定で、%category%/%postname% みたいなことをやっていると、カテゴリが複数登録された記事の場合、勝手にカテゴリーIDの一番小さいものを選んで使用します。

カテゴリーが子カテゴリを持っていて、どっちにも登録したいような場合、カテゴリIDの大小によって親カテゴリが使われたり子カテゴリが使われたり、といった感じで統一感に欠けた振る舞いになります。
ページのURL階層のように、常に一番深い階層を使うか、あるいは常に親カテゴリを使うか、どっちかに統一して欲しいわけです。
しかし、カテゴリーIDは勝手に変更できない。(まあ、データベースを弄っちゃう、という荒技もあるけど)

というわけで、これを、自分の選んだカテゴリで表示するよう変更するためのフィルターフックを書いてみました。

  1. 記事のpost_metaに、permalink_cat_idというキーでカテゴリIDが登録されていた場合は、それを使う
  2. permalink_cat_idフィールドが空の場合もしくはそもそも登録されていない場合は、wordpressのデフォルトの表記に従う(つまりカテゴリIDが一番小さなものが使われる)

というような振る舞いです。

post_metaにカテゴリIDを登録する方法は色々ありますが、私はCustom Field Gui Utilityというプラグインを使わせてもらっています。

ちなみに、お目当てのカテゴリIDを知るには、カテゴリ編集ページに行って、URLを見ると埋め込まれています。
もっとも、私はAdminerというプラグインを入れて、直接データベースを見に行ってますが。

カテゴリIDの代わりにカテゴリスラッグを使う、というのも考えたのですが、ただでさえフックかけてコスト高いのに、あんまり重い作業をさせるのは嫌だな、と思って止めました。(自分結構スラッグ変更するし)
一度設定したスラッグは絶対変えない、というSEO達人の皆様は、そのへん仕様変更されたら良いと思います。


関数はほとんどget_permalink関数上書きに近いですが、こんな感じ。
あんまりphpは書き慣れてないので、あまりスマートなコードではないかも。
動作確認きちんとやってませんので、ご使用は自己責任でお願いします。

/**
 * 記事に複数カテゴリを登録し、パーマリンクが
 * %category%/%postname% の場合に自分で選んだカテゴリで
 * 表示させる
 */

function hmo_modify_permalink($permalink, $post, $leavename)
{

 /* 書き換えの必要がない場合は即関数を抜ける */
  if ( empty($post->ID) || 
       $post->post_type == 'page' || 
       $post->post_type == 'attachment' || 
       in_array($post->post_type, get_post_types( 
                                array('_builtin' => false) ) ) ) 
  {
       return $permalink;
  }
  
  /* post_metaのparmalink_cat_idにカテゴリIDが登録されていない場合
     は、デフォルトのパーマリンクを使う */
  $permalink_cat_id = '';
  $permalink_cat_id = get_post_meta($post->ID, 'permalink_cat_id', true);
  if ($permalink_cat_id == '') {
       return $permalink;
  }

  /* パーマリンク作成、以下はほとんど wp-include/link-template.php
     のget_permalink関数のコピー */
  
  $rewritecode = array(
                '%year%',
                '%monthnum%',
                '%day%',
                '%hour%',
                '%minute%',
                '%second%',
                $leavename? '' : '%postname%',
                '%post_id%',
                '%category%',
                '%author%',
                $leavename? '' : '%pagename%',
  );

  $permalink_str = get_option('permalink_structure');

  if ( '' != $permalink_str && !in_array($post->post_status,
                     array('draft', 'pending', 'auto-draft')) ) {
       $unixtime = strtotime($post->post_date);

       $category = '';
       if ( strpos($permalink_str, '%category%') !== false ) {
       
             /* post_metaのparmalink_cat_idに登録されたidの
                カテゴリを使う */
             if ( $permalink_cat = get_category($permalink_cat_id) ) {
                  $category = $permalink_cat->slug;
                  if ( $parent = $permalink_cat->parent ) {
                       $category = get_category_parents($parent,
                                   false, '/', true) . $category;
                  }
             }
             // show default category in permalinks, without
             // having to assign it explicitly
             if ( empty($category) ) {
                   $default_category = get_category( 
                               get_option( 'default_category' ) );
                   $category = is_wp_error( $default_category ) ?
                                     '' : $default_category->slug;
             }
       }

       $author = '';
       if ( strpos($permalink_str, '%author%') !== false ) {
             $authordata = get_userdata($post->post_author);
             $author = $authordata->user_nicename;
       }

       $date = explode(" ",date('Y m d H i s', $unixtime));
       $rewritereplace =
                       array(
                        $date[0],
                        $date[1],
                        $date[2],
                        $date[3],
                        $date[4],
                        $date[5],
                        $post->post_name,
                        $post->ID,
                        $category,
                        $author,
                        $post->post_name,
                );
                
        /* get_permalink関数で生成した $permalinkをここで上書き */
        $permalink = home_url( str_replace($rewritecode,
                               $rewritereplace, $permalink_str) );
        $permalink = user_trailingslashit($permalink,
                                          'single');        
   } 
   return $permalink;
}

/*テンプレートタグの <?php get_permalink() > が返すリンクを
上書きするフックをかける*/

add_filter( 'post_link', 'hmo_modify_permalink', 10, 3 );

以上。
このコードをfunction.phpに書いておけば、で生成されるパーマリンクが上記の仕様で変更(上書き)されます。