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が回遊されへんやんけ!やり直せ!」とキレ気味に言われたことを思い出した。(苦笑)

まぁこれは昔も今もずっと変わらないことだし、やっぱりサイトに必要なハイパーリンク関係は、何が何でも最低限、実体ソースに書き出せるようにしとかんとね。

カテゴリ

この記事のコメント

  1. いつも拝見させて頂いております。
    岩井と申します。

    simplicity-add-fields-search-engineについて質問がございます。
    現在、こちらで新たなサイトを作成途中なのですが、
    http://kaz-academy.com/campus/

    親カテゴリー:AAA
     子カテゴリー:BBB
     子カテゴリー:CCC
    親カテゴリー:aaa
     子カテゴリー:bbb
     子カテゴリー:ccc
    親カテゴリー:111
     子カテゴリー:222
     子カテゴリー:333

    という関係の時に、タクソノミーの検索で、

    親カテゴリー:AAA ▼
    親カテゴリー:aaa ▼
    親カテゴリー:111 ▼

    上記のようにプルダウンで子カテゴリーを表示させたいと思っております。
    現在はテーマファイルにを記述していて、今後の管理を含めると可能な限りハードコーディングでの記述は避けたいと思っております。

    もし可能であれば方法をご教授頂けないでしょうか。
    またお仕事としてご依頼させていただく事が可能でしたら、おおまな費用を教えていただけたら幸いです。

    お手数おかけしますが、何卒よろしくお願い申し上げます。

    1. コメントありがとうございます。
      繁忙期故に実に2週間強以上お待たせした亀レスとなってしまい大変申し訳ございません。
      テーマファイルに記述しているということで、今回はそれを対象としたご回答をいたします。

      「Simplicity Add Fields Search Engine」ではカテゴリ(=ターム)の絞り込みについては標準の状態だとinputタグのcheckboxになっております。
      ただし、タームそのものは基本的にtid[]をベースにデータを絞り込んでおり、これをプルダウンで(selectタグを例として)代替する場合、

      <select name=”tid[]”><!–tid[0]でも可–>
      <option value=””>選択</option>
      <option value=”AAAのタームID”>AAA</option>
      <option value=”BBBのタームID”>BBB</option>

      </select>

      <select name=”tid[]”><!–tid[1]でも可–>
      <option value=””>選択</option>
      <option value=”aaaのタームID”>aaa</option>
      <option value=”bbbのタームID”>bbb</option>

      </select>

      <select name=”tid[]”><!–tid[2]でも可–>
      <option value=””>選択</option>
      <option value=”111のタームID”>111</option>
      <option value=”222のタームID”>222</option>

      </select>

      以上の形が基本的な書き方となると思います。

      また、ハードコーディングというのは恐らくタームの決め打ちのことをおっしゃられているかと思います。(間違っていたらすみません。)
      動的なタームのリスティング、すなわちselectタグ及びselect内のoptionに関しては、WordPressのテンプレート関数get_terms()辺りを利用してselectタグ様式で親子をループ出力してみて下さい。

      複数のタクソノミーも動的に出力する場合は、WPのCodex等でget_taxonomies()辺りも参考になるかと思います。
      ループのルーチンに関してはタクソノミー→タームで行う場合、get_taxonomies()でターム情報を取得、取得したタクソノミー種別等をget_terms()のパラメータにキックして処理してみて下さい。
      また、画面の遷移が行われた後のselectタグの選択状態については、タームのループ(つまりoption出し)の際に、「Simplicity Add Fields Search Engine」でのget送信パラメータのtid[]の配列状態を、例えば$tid = $_GET[‘tid’]; if ( in_array( $tid, タームID ) ) {…}と言った形で解析・比較し、ループ内でタームIDが現れた際にoptionへselected=”selected”を付与することでひとまずは対応可能かと思います。

      WordPressの場合、タームIDは親子関係があったりタクソノミーを跨いだとしても常に一意のIDなので、以上で恐らく問題はないかと思います。
      (ただし、本来selectでの入力を想定しておりませんので、URIのパラメータには余分にtid[]が入ることを確認しております。予めご注意下さい。)

      なお、本ブログは普段の仕事の隙間に仕事の時間を忘れたいがために自由帳的に綴っている備忘録と雑記(と私的事、作ったものの公開所)になります。
      お仕事の話は非常に大変ありがたいのですが、自由気ままに書いていくというコンセプトを縛らないために、大変申し訳ありませんが本ブログからのお仕事のご依頼は現在のところ基本的に行っておりません。
      ただ、こちらの可能な範囲でのお問い合わせ・ご質問等があれば、お返事いたします。

      …そんな感じのブログなので、コメントいただく際にお名前は*ニックネーム*や*イニシャル*等で結構ですよ!(`・ω・´)
      (誠に勝手ながら、フルネームが入力されておりましたのでご姓に敬称をお付けする形に変更しました。)

  2. cattlemute様

    詳しくご教授頂き誠にありがとうございました。
    あれから素人ながら教えて頂いた内容を基に試行錯誤をしているのですが、どうしても画面遷移後の選択状態がselectedにならず、困っています。。
    下記URLなのですが、
    https://kaz-academy.com/campus/
    例として学校種別の部分は下記phpのように記述しております。

    <input type="checkbox" name="tid[]" value="term_id; ?>” class=”safse-term-checkbox”>
    name; ?>

    コメントで対応方法について丁寧に教えて頂いたのですが、当方の実力不足で理解が及ばず、ご迷惑をおかけして申し訳ございません。
    もう一度何かヒントを頂けないでしょうか。また、参考になるようなサイトなどございましたら教えて頂けたら幸いです。

    お忙しい所、大変恐縮ですが何卒よろしくお願い申し上げます。

    1. コメント返信ありがとうございます。

      いただきましたコードを拝見するに、’school_type’という一種類のタクソノミーでタームのループを回されているようですね。
      御社が作成されている投稿タイプとタクソノミーの構成がどのような形で、タームの構成がどのような形になっているかが分かり兼ねるのでなんともですが…。
      サンプルでは教科・偏差値等々が分けられていますが、その中で学校種別が’school_type’に該当すると考えると、これはある投稿タイプに対して複数のタクソノミーが紐付けられている…と言った解釈でよろしいでしょうか。

      …もしその形であれば、cattlemuteがざっくり書くと以下のようになると思います。

      // 出力用変数
      $output = '';
      
      // $_GETのtidを取得して変数$tidへ格納(selectedの照合に使用するため)
      $tid = array();
      if ( isset( $_GET['tid'] ) && is_array( $_GET['tid'] ) ) $tid = $_GET['tid'];
      
      // タクソノミー情報のオブジェクトを取得
      $taxes = get_taxonomies( array(
      	// 表に出ているタクソノミーのみに限定
      	'public' => true,
      	// object_typeで投稿タイプを指定
      	// 下記の例はWP標準の投稿である'post' ここに指定した投稿タイプのタクソノミーを以降のselectに取得される
      	'object_type' => array( 'post' ),
      ), 'objects' ); 
      
      // タクソノミーのループ
      if ( $taxes ) :
      	foreach ( $taxes as $tax ) :
      		// タクソノミー内のターム情報を取得
      		$terms = get_terms( array(
      			'taxonomy' => $tax->name,
      			'hide_empty' => false,
      		) );
      
      		// タームのループ
      		if ( $terms ) :
      			// selectとoptionの生成ここから
      			$output .= '<p class="tax-' . $tax->name . '">';
      			$output .= $tax->label . ':';
      			$output .= '<select name="tid[]">';
      			$output .= '<option value="">選択して下さい</option>';
      			foreach ( $terms as $term ) :
      				$output .= '<option value="' . $term->term_id . '"';
      
      				// 【超・重要!】$tid($_GET['tid']の格納先)をin_array()でループ中のタームIDと照合してselectedを付与
      				if ( in_array( intval( $term->term_id ), $tid ) ) :
      					$output .= ' selected="selected"';
      				endif;
      
      				$output .= '>' . $term->name . '</option>';
      			endforeach;
      			$output .= '</select>';
      			$output .= '</p>';
      			// selectとoptionの生成ここまで
      		endif;
      	endforeach;
      endif;
      
      // selectタグを出力
      echo $output;
      
  3. 丁寧にご返信ありがとうございます!
    ご教授頂いた通りにして、無事にやりたかった事できて本当に感謝の念でいっぱいです。
    業務で大変お忙しい中、ご協力いただきまして本当にありがとうございました!

    1. いえいえ。お役に立てたようでよかったです。
      また何かあればどうぞ!今後とも本ブログをよろしくお願いいたします(´・ω・`)

コメントを残す

メールアドレスが公開されることはありません。