Custom Post Type Widgetsにも年ベースの月別アーカイブを表示する
現在やっている案件で、ブログを通常投稿タイプとカスタム投稿タイプで2つに分けた際に、ウィジェットでそれぞれの月別アーカイブを設置するため、Custom Post Type Widgetsのプラグインを使用することにした。(アーカイブのパーマリンクパターンを設定するためにCustom Post Type Permalinksも併用している。)
まぁ、普通にプラグインに機能に則って単純に月別アーカイブだけ表示しとけばいーじゃんという話なんだけど、ここの間ずっと案件の中で常態化している「年ベースの月別アーカイブ」が例にもれずにデザインの中に組み込まれて出てきたため、「ああまたか」と少し辟易。
以前、こちらの記事でWordPressのデフォルトの月別アーカイブウィジェットで、年ベースの月別アーカイブを表示させるための記事を書いたが、デフォルト外のウィジェットには当然この内容は適用されない。
まぁ、Custom Post Type Widgetsのウィジェット用のクラスの継承を行っていないから当然なんだけど。
そんなわけで今回対応してみた。
まず、Custom Post Type Widgetsのプラグインのソースから、月別アーカイブを出しているウィジェットを出しているクラスを探す必要がある。
幸いなことにこのプラグインは、相当シンプルに作られており「custom-post-type-widgets/inc/widget-custom-post-type-archive.php」とすぐに見つかった。
このPHPファイルの中の「WP_Custom_Post_Type_Widgets_Archives」でカスタム投稿タイプの月別アーカイブを表示している。
そして、ここから、WordPressのデフォルト月別アーカイブのウィジェットと同じような書き方をしているメソッドを探し出してくる。
といっても、これも基本はWP_Widgetのクラスを継承してメソッドをオーバーライドしているだけ。
なので、一行目から見ていってもすぐにわかる。めんどくさければ「public function widget」でソースコード内を検索すればおk。
次に、このpublic function widgetのメソッドの内容をコピーし、テーマのfunctions.phpを開く。
テーマ適用時にプラグインが入っていない状態も想定して、class_existをかけて以下のような感じでコピーしたソースを突っ込む。
if( class_exists( 'WP_Custom_Post_Type_Widgets_Archives' ) ) { class WP_Custom_Post_Type_Widgets_Archives_Override extends WP_Custom_Post_Type_Widgets_Archives { $posttype = ! empty( $instance['posttype'] ) ? $instance['posttype'] : 'post'; $c = ! empty( $instance['count'] ) ? '1' : '0'; $d = ! empty( $instance['dropdown'] ) ? '1' : '0'; $title = apply_filters( 'widget_title', empty( $instance['title'] ) ? __( 'Archives', 'custom-post-type-widgets' ) : $instance['title'], $instance, $this->id_base ); // ~~~~略~~~~ <?php } // ~~~↓↓↓後で書き換える↓↓↓~~~ else { ?> <ul> <?php wp_get_archives( apply_filters( 'widget_archives_args', array( 'post_type' => $posttype, 'type' => 'monthly', 'show_post_count' => $c, ) ) ); ?> </ul> <?php } // ~~~↑↑↑後で書き換える↑↑↑~~~ remove_filter( 'month_link', array( $this, 'get_month_link_custom_post_type' ) ); remove_filter( 'get_archives_link', array( $this, 'trim_post_type' ) ); echo $args['after_widget']; } }
次に、こちらの記事で書いていたClass YearlyBase_Monthly_Archivesをこれまたfunctions.phpにコピペ。
その後、上でプラグインからコピーして突っ込んだコードの「後で書き換える」とした部分をこのように変更する。
else { $archives_class = new YearlyBase_Monthly_Archives(); $yearlies = $archives_class->explode_of_year( apply_filters( 'widget_archives_args', array( 'post_type' => $posttype, 'show_post_count' => $c, ), $instance ) ); foreach ( $yearlies as $yearly ) { $archives_class->monthly_on_year_with_title( apply_filters( 'widget_archives_args', array( 'post_type' => $posttype, 'show_post_count' => $c, ), $instance ), $yearly ); } }
こちらの記事で書いていたコードと違うところは’post_type’の指定を追加したとこくらい。
$posttypeにはCustom Post Type Widgetsで管理画面側から設定した投稿タイプのオプション値が入っている。
とりあえず、プラグインのウィジェットクラスのオーバーライドはこんなところ。
ただ、これだけをそのまましたあとにウィジェットの動作を確認したところ問題が発覚。
冒頭でカッコ書きで触れたCustom Post Type Permalinksを併用している場合、年別アーカイブの表示やリンクはともかく、その下に続く月別アーカイブのリンクがおかしくなった。
具体的に言うと「投稿タイプ識別/YYYY/MM」と続くはずのものが、「投稿タイプ識別/投稿タイプ識別/YYYY/MM」となってしまっている。
調べてみたところ、Custom Post Type Widgetsでソースコードに書かれている「wp_get_archives」へのフィルタと「trim_post_type」のメソッド、Custom Post Type Permalinksで設定したパーマリンク設定によるリライトの相性が悪いらしい。
そのため、Class YearlyBase_Monthly_Archives側に対策を入れておく。
このクラスのmonthly_on_year_with_titleメソッドに少しだけ追記する。
public function monthly_on_year_with_title( $_args = false, $_year = '' ) { $_echo = 1; if ( ! $_args || ! is_array( $_args ) ) $_args = array( 'type' => 'monthly', 'echo' => 0, ); if ( ! isset( $_args['echo'] ) ) $_args['echo'] = 1; $_echo = $_args['echo']; $_args['type'] = 'monthly'; $_args['echo'] = 0; $_year = $this->convert_wp_get_archives_year( $_year ); $this->set_year( $_year ); add_filter( 'getarchives_where', array( $this, 'durationize' ), 10, 2 ); $_yearly = wp_get_archives( array_merge( $_args, array( 'type' => 'yearly' ) ) ); $_yearly = preg_replace( '~<li>|<li.+?>|</li>~', '', $_yearly ); remove_filter( 'getarchives_where', array( $this, 'durationize' ) ); $_monthly = '<ul class="children">' . $this->monthly_on_year( $_args, $_year ) . '</ul>'; // ↓↓↓これを追記↓↓↓ if ( ! empty( $_args['post_type'] ) ) $_monthly = str_replace( $_args['post_type'] . '/' . $_args['post_type'], $_args['post_type'], $_monthly ); // ↑↑↑これを追記↑↑↑ if ( 0 == $_echo ) return '<ul><li>' . $_yearly . $_monthly . '</li></ul>'; elseif ( 1 == $_echo ) echo '<ul><li>' . $_yearly . $_monthly . '</li></ul>'; }
post_typeに値があるときだけ、リンクの文字列を置換するようにする。
これで表示を確認したところ、Custom Post Type Widgetsのカスタム投稿タイプの月別アーカイブウィジェットを、年ベースの月別アーカイブウィジェットとして扱うことができた。
WordPressデフォルトのウィジェットとCustom Post Type Widgetsのウィジェットで差異がどれだけあるかが非常に気になっていたが、おおよそ大きな差異がなかったので良かったと思う。
で、いつものごとくよもやま話。
実は、今回のこの記事の作業を実際に行う前に、めんどいことしたくねーなと思って、似たようなことを仕事の関係のとあるところでもやってたっけと思い出して見てみたのだが・・・
JavaScriptで単純なDOM操作して、間接的に年アーカイブのリストタグを突っ込んでた。なるほど、全然参考にならん。
見る人が見るとかなり恥ずかしい方法だったのでパス。
3秒でそっとじしました。(´・ω・`)
はるかむかーし、JavaScriptオンリーで実体HTMLを持たさずにナビゲーションを突っ込んだとき、「これじゃ、何を持ってもいちばん重要なHTMLテキスト単体でURLが回遊されへんやんけ!やり直せ!」とキレ気味に言われたことを思い出した。(苦笑)
まぁこれは昔も今もずっと変わらないことだし、やっぱりサイトに必要なハイパーリンク関係は、何が何でも最低限、実体ソースに書き出せるようにしとかんとね。