WordPress パンくずリストの作成
プラグインを使わずに functions.php に記述してパンくずリストを作成する方法と JSON-LD で構造化マークアップを出力する方法の覚え書きです。
以前ブログに掲載したパンくずリストを出力するコードを更新・修正(※)したものと、それを基に JSON-LD で構造化マークアップを出力するコードを掲載しています。
[追記] 2019年07月10日 コードの内容を一部変更しました。
更新日:2022年03月13日
作成日:2019年07月04日
パンくずリストのコード
以下のパンくずリストのコードは一応機能すると思いますが、限られた条件でしか確認できていません。
また、カスタム投稿タイプの日付アーカイブページには未対応です。
表示設定の「ホームページの表示」で「固定ページ」を選択している場合、デフォルトでは「ホームページ」に指定した固定ページでは「Home」と表示し、「投稿ページ(メインブログページ)」に指定した固定ページでは「Blog」と表示します。
ホームページやメインブログページで表示する文字はそれぞれオプションで変更可能で、非表示にすることもできます。
投稿やカスタム投稿タイプの個別ページでは現在のページをオプションで非表示にすることができます。
また、デフォルトでは投稿やカスタム投稿タイプの個別ページではカテゴリーやタームに属していればそれらを階層的に表示します。
デフォルトでは1つの投稿が複数のカテゴリーやタームに属している場合、どのターム(カテゴリー)を表示するかを指定することができないため、期待通りに表示されない可能性があります。
オプションでカテゴリーやターム(または親だけ)を非表示にすることが可能です。
※2019年07月10日 追記
オプションでカスタムフィールドを使って優先するカテゴリーやタームを選択可能にしました。また複数のカスタムタクソノミーを使用している場合は、カスタムフィールドを使って表示対象のカスタムタクソノミーを選択可能にしました。
パンくずリストのマークアップは、デフォルトでは以下のような nav 要素 と ul li 要素で出力します。
オプションで nav 要素の代わりに div 要素を指定可能です。各要素のクラスなどの属性はパラメータのオプションを指定すると出力します。
<nav> <ul> <li><a href="http://example.com/">Home</a></li> <li class="separator"> > </li> <li>News</li> </ul> </nav>
/*** パンくずリストを出力する関数 my_breadcrumbs 2019/07/14 updated *** * 2019/07/14 カスタム投稿タイプにカスタム分類が登録されていない場合に対応(161行目追加) * * カスタム投稿タイプ日付アーカイブには未対応。 * * カスタムフィールド myterm を使って優先するカテゴリーやタームを指定可能に 2019/07/10 * * [注意]この関数は別途 get_deepest_term() が必要です。 * * パラメータ $args :引数の連想配列(全てオプション)。以下概要。 * nav_div リスト(ul 要素)を囲む要素を指定。デフォルト: nav(要素) * aria_label リストを囲む要素に指定する aria_label 属性。値を指定すると出力 * id リストを囲む要素に指定する id 属性。値を指定すると出力 * nav_div_class リストを囲む要素に指定する class 属性。値を指定すると出力 * ul_class ul 要素に指定する class 属性。値を指定すると出力 * li_class リンクを含む li 要素に指定する class 属性。値を指定すると出力 * li_active_class リンクを含まない li 要素に指定する class 属性。値を指定すると出力 * aria_current リンクを含まない li 要素に指定する aria-current 属性。値を指定すると出力 * show_home ホームの場合にホームの文字列を表示するかどうか。初期値 true(表示する) * show_current 個別投稿ページの場合に現在のページを表示するかどうか。 初期値 true(表示する)2019/07/06 Added * show_cpta カスタム投稿タイプ個別ページでアーカイブページを表示するかどうか。 初期値 true(表示する)2019/07/06 Added * home ホームの文字列。初期値:Home * blog_home 管理画面のホームページの表示で「固定ページ」→「投稿ページ(メインブログページ)」に * 指定したページで表示する文字。初期値:Blog * search 検索結果ページの文字列。初期値: で検索した結果 * tag タグページの文字列。初期値:タグ : * author 投稿者ページの文字列。初期値:投稿者 * notfound 404ページの文字列。初期値: 404 Not found * separator 区切り文字列。''を指定すると出力しない。初期値:<li class="separator"> > </li> * cat_off 個別ページでカテゴリーを表示しない。初期値: false(表示する) * cat_parents_off 個別ページで親のカテゴリーを表示しない。初期値: false(表示する) * tax_off カスタム投稿タイプ個別ページでタームを表示しない。初期値:false(表示する) * tax_parents_off カスタム投稿タイプ個別ページで親のタームを表示しない。初期値:false(表示する) * show_cat_tag_for_cpt カスタム投稿タイプ個別ページでカテゴリーを表示する(その場合は、カスタムタクソノミーは表示されない)。初期値:false(表示しない) * 記事が複数のカテゴリーやタームに属する場合、カスタムフィールド(myterm)を使って優先するカテゴリーやタームを指定可能 * カスタム投稿タイプに複数のカスタムタクソノミーが登録されている場合、カスタムフィールド(my_pref_tax)を使ってタクソノミーを指定可能 */ function my_breadcrumbs($args = array()){ global $post; // デフォルトの値 $defaults = array( 'nav_div' => 'nav', 'aria_label' => '', 'id' => '', 'nav_div_class' => '', 'ul_class' =>'', 'li_class' => '', 'li_active_class' => '', 'aria_current' => '', 'show_home' => true, 'show_current' => true, 'home' => 'Home', 'blog_home' => 'Blog', 'search' => 'で検索した結果', 'tag' => 'タグ : ', 'author' => '投稿者', 'notfound' => '404 Not found', 'separator' => "\n".'<li class="separator"> > </li>'."\n", 'cat_off' => false, 'cat_parents_off' => false, 'tax_off' => false, 'tax_parents_off' => false, 'show_cpta' => true, 'show_cat_tag_for_cpt' => false, ); //引数の値とデフォルトをマージ $args = wp_parse_args( $args, $defaults ); //マージした値を変数として抽出 extract( $args, EXTR_SKIP ); //マージした値を元に出力するかどうかを設定 $aria_label = $aria_label ? ' aria-label="' .$aria_label . '" ' : ''; $id = $id ? ' id="' .$id . '" ' : ''; $nav_div_class = $nav_div_class ? ' class="' .$nav_div_class . '" ' : ''; $ul_class = $ul_class ? ' class="' .$ul_class . '" ' : ''; $li_class = $li_class ? ' class="' .$li_class . '" ' : ''; $li_active_class = $li_active_class ? ' class="' .$li_active_class . '" ' : ''; $aria_current = $aria_current ? ' aria-current="' .$aria_current . '"' : ''; //パンくずリストのマークアップ文字列の初期化 $str =''; //ホーム・フロントページの場合 if(is_front_page() || is_home()) { if($show_home) { $label = is_front_page() ? $home: $blog_home; echo '<'.$nav_div . $id . $nav_div_class. $aria_label. '><ul'. $ul_class .'><li'. $li_active_class. $aria_current. '>'. $label .'</li></ul></'. $nav_div .'>'; } } //ホーム・フロントページでない場合(且つ管理ページでない場合) if(!is_front_page() && !is_home() && !is_admin()){ //ホームへのリンクを含むリストを生成 $str.= '<'.$nav_div . $id . $nav_div_class. $aria_label.'>'."\n"; $str.= '<ul'. $ul_class .'>'."\n"; $str.= '<li'. $li_class .'><a href="'. home_url() .'/">'. $home .'</a></li>'; //$wp_query の query_vars から get_query_var() でクエリ変数の値を取得 //タクソノミー名を取得(タクソノミーアーカイブの場合のみ取得可能) $my_taxonomy = get_query_var('taxonomy'); //投稿タイプ名を取得(カスタム投稿タイプ個別ページの場合のみ取得可能) $cpt = get_query_var('post_type'); //カスタムタクソノミーアーカイブページ //タクソノミー名が取得できて且つカスタムタクソノミーアーカイブページの場合 if($my_taxonomy && is_tax($my_taxonomy)) { //タームオブジェクト(現在のページのオブジェクト)を取得 $my_term = get_queried_object(); //タクソノミーの object_type プロパティは配列 $post_types = get_taxonomy( $my_taxonomy )->object_type; //配列の0番目からカスタム投稿タイプのスラッグ(カスタム投稿タイプ名)を取得 $cpt = $post_types[0]; //get_post_type_archive_link():指定した投稿タイプのアーカイブページのリンク //get_post_type_object($cpt)->label:指定した投稿タイプのオブジェクトのラベル(名前) //カスタム投稿のアーカイブページへのリンクを追加 $str.= $separator; $str.='<li'. $li_class .'><a href="' .esc_url(get_post_type_archive_link($cpt)).'">'. get_post_type_object($cpt)->label.'</a></li>'; //タームオブジェクトに親があればそれらを取得してリンクを生成してリストに追加 if($my_term->parent != 0) { //祖先タームオブジェクトの ID の配列を取得し逆順に(取得される配列の並びは階層の下から上) $ancestors = array_reverse(get_ancestors( $my_term->term_id, $my_term->taxonomy )); //全ての祖先タームオブジェクトのアーカイブページへのリンクを生成してリストに追加 foreach($ancestors as $ancestor){ $str.= $separator; $str.='<li'. $li_class .'><a href="'. esc_url(get_term_link($ancestor, $my_term->taxonomy)) .'">'. get_term($ancestor, $my_term->taxonomy)->name .'</a></li>'; } } //ターム名を追加 $str.= $separator; $str.='<li' .$li_active_class. $aria_current.'>'. $my_term->name . '</li>'; //カテゴリーのアーカイブページ }elseif(is_category()) { //カテゴリーオブジェクトを取得 $cat = get_queried_object(); //取得したカテゴリーオブジェクトに親があればそれらを取得してリンクを生成してリストに追加 if($cat->parent != 0){ $ancestors = array_reverse(get_ancestors( $cat->term_id, 'category' )); foreach($ancestors as $ancestor){ $str.= $separator; $str.='<li'. $li_class .'><a href="'. esc_url(get_category_link($ancestor)) .'">'. get_cat_name($ancestor) .'</a></li>'; } } //カテゴリー名を追加 $str.= $separator; $str.='<li' .$li_active_class. $aria_current.'>'. $cat->name . '</li>'; //カスタム投稿のアーカイブページ }elseif(is_post_type_archive()) { //カスタム投稿タイプ名を取得 $cpt = get_query_var('post_type'); //カスタム投稿タイプ名を追加 $str.= $separator; $str.='<li' .$li_active_class. $aria_current.'>'. get_post_type_object($cpt)->label . '</li>'; //カスタム投稿タイプの個別記事ページ }elseif($cpt && is_singular($cpt)){ if($show_cpta) { //カスタム投稿タイプアーカイブページへのリンクを生成してリストに追加 $str.= $separator; $str.='<li'. $li_class .'><a href="' .esc_url(get_post_type_archive_link($cpt)).'">'. get_post_type_object($cpt)->label.'</a></li>'; } //このカスタム投稿タイプに登録されている全てのタクソノミーオブジェクトの名前を取得 $taxes = get_object_taxonomies( $cpt ); //タクソノミーオブジェクトの名前が取得できれば if(count($taxes) !== 0) { //タクソノミーを表示する場合 if(!$tax_off) { //配列の先頭のタクソノミーオブジェクトの名前(複数ある可能性があるので先頭のものを使う) //デフォルトでは標準のカテゴリーやタグが追加されている場合はインデックスを変更 //但し、show_cat_tag_for_cpt が true の場合はカテゴリーを取得可能に $tax_index = 0; if(!$show_cat_tag_for_cpt) { for ($i = 0; $i < count($taxes); $i++) { if($taxes[$i] !== 'category' && $taxes[$i] !== 'post_tag' && $taxes[$i] !== 'post_format') { $tax_index = $i; break; } } } $mytax = $taxes[$tax_index] ? $taxes[$tax_index] : null; //カスタムフィールドに優先するタクソノミーのラベルが記載されていればそのタクソノミーを選択 //タクソノミーのラベルを取得 $my_pref_tax_label = get_post_meta( get_the_ID(), 'my_pref_tax', true) ? esc_attr(get_post_meta( get_the_ID(), 'my_pref_tax', true)) : null; //ラベルからタクソノミーを取得(戻り値はタクソノミーの名前の配列) $my_pref_tax_name = get_taxonomies(array('label'=> $my_pref_tax_label)); //タクソノミー名の初期化 $my_pref_tax = ''; //取得した配列が1つの場合、その値が優先されるタクソノミーの名前 if(count($my_pref_tax_name) == 1 ){ $my_pref_tax = $my_pref_tax_name[key($my_pref_tax_name)]; } //タクソノミーの名前が取得できて且つそのタクソノミーが現在の投稿タイプに属している場合は、そのタクソノミーを使用 if($my_pref_tax && is_object_in_taxonomy($post->post_type, $my_pref_tax)) { $mytax = $my_pref_tax; } //投稿に割り当てられたタームオブジェクト(配列)を取得 $terms = get_the_terms($post->ID, $mytax); //カスタムフィールドに優先するタームが記載されていればその値を取得して $myterm へ $myterm = get_post_meta( get_the_ID(), 'myterm', true) ? esc_attr(get_post_meta( get_the_ID(), 'myterm', true)) : null; //$terms が取得できていれば一番下の階層のタームを取得(できない場合は null に) $my_term = $terms ? get_deepest_term($terms, $mytax, $myterm) : null; //タームが取得できていれば if( !empty($my_term) ) { //$tax_parents_off がfalse(初期値)でタームに親があればそれらを取得してリンクを生成してリストに追加 if($my_term->parent != 0 && !$tax_parents_off){ $ancestors = array_reverse(get_ancestors( $my_term->term_id, $mytax )); foreach($ancestors as $ancestor){ $str.= $separator; $str.='<li'. $li_class .'><a href="'. esc_url(get_term_link($ancestor, $mytax)).'">'. get_term($ancestor, $mytax)->name . '</a></li>'; } } //タームのリンクを追加 $str.= $separator; $str.='<li'. $li_class .'><a href="'. esc_url(get_term_link($my_term, $mytax)).'">'. $my_term->name . '</a></li>'; } } } if($show_current) { $str.= $separator; //$post->post_title には HTML タグが入っている可能性があるのでタグを除去 //wp_strip_all_tags() の代わりに PHP の strip_tags() でも $str.= '<li' .$li_active_class. $aria_current.'>'. wp_strip_all_tags($post->post_title) .'</li>'; } //個別投稿ページ(添付ファイルも true と判定されるので除外) }elseif(is_single() && !is_attachment()){ //投稿が属するカテゴリーオブジェクトの配列を取得 $categories = get_the_category($post->ID); //カテゴリーを表示する場合 if(!$cat_off) { //カスタムフィールドに優先するカテゴリーが記載されていればその値を取得して $myterm へ $myterm = get_post_meta( get_the_ID(), 'myterm', true) ? esc_attr(get_post_meta( get_the_ID(), 'myterm', true)) : null; //一番下の階層のカテゴリーを取得 $cat = get_deepest_term($categories, 'category', $myterm); //$cat_parents_off が false(初期値)でカテゴリーに親があればそれらを取得してリンクを生成してリストに追加 if($cat->parent != 0 && !$cat_parents_off){ $ancestors = array_reverse(get_ancestors( $cat->term_id, 'category' )); foreach($ancestors as $ancestor){ $str.= $separator; $str.='<li'. $li_class .'><a href="'. esc_url(get_category_link($ancestor)).'">'. get_cat_name($ancestor). '</a></li>'; } } //カテゴリーのリンクを追加 $str.= $separator; $str.='<li'. $li_class .'><a href="'. esc_url(get_category_link($cat->term_id)). '">'. $cat->name . '</a></li>'; } if($show_current) { $str.= $separator; $str.= '<li' .$li_active_class. $aria_current.'>'. wp_strip_all_tags($post->post_title) .'</li>'; } //固定ページ } elseif(is_page()){ //固定ページに親があればそれらを取得してリンクを生成してリストに追加 if($post->post_parent != 0 ){ $ancestors = array_reverse(get_post_ancestors( $post->ID )); foreach($ancestors as $ancestor){ $str.= $separator; $str.='<li'. $li_class .'><a href="'. esc_url(get_permalink($ancestor)).'">'. get_the_title($ancestor) .'</a></li>'; } } //固定ページ名を追加 $str.= $separator; $str.= '<li' .$li_active_class. $aria_current.'>'. wp_strip_all_tags($post->post_title) .'</li>'; //日付ベースのアーカイブページ } elseif(is_date()){ //年別アーカイブ if(get_query_var('day') != 0){ //日付アーカイブページでは get_query_var() でアーカイブページの年・月・日を取得できる //取得した値と get_year_link() などを使ってリンクを生成 $str.= $separator; $str.='<li'. $li_class .'><a href="'. get_year_link(get_query_var('year')). '">' . get_query_var('year'). '年</a></li>'; $str.= $separator; $str.='<li'. $li_class .'><a href="'. get_month_link(get_query_var('year'), get_query_var('monthnum')). '">'. get_query_var('monthnum') .'月</a></li>'; $str.= $separator; $str.='<li' .$li_active_class. $aria_current.'>'. get_query_var('day'). '日</li>'; //月別アーカイブ } elseif(get_query_var('monthnum') != 0){ $str.= $separator; $str.='<li'. $li_class .'><a href="'. get_year_link(get_query_var('year')) .'">'. get_query_var('year') .'年</a></li>'; $str.= $separator; $str.='<li' .$li_active_class. $aria_current.'>'. get_query_var('monthnum'). '月</li>'; //年別アーカイブ } else { $str.= $separator; $str.='<li' .$li_active_class. $aria_current.'>'. get_query_var('year') .'年</li>'; } //検索結果表示ページ } elseif(is_search()) { $str.= $separator; $str.='<li' .$li_active_class. $aria_current.'>「'. get_search_query() .'」'. $search .'</li>'; //投稿者のアーカイブページ } elseif(is_author()){ $str.= $separator; $str .='<li' .$li_active_class. $aria_current.'>'. $author .' : '. get_the_author_meta('display_name', get_query_var('author')).'</li>'; //タグのアーカイブページ } elseif(is_tag()){ $str.= $separator; //$str.='<li' .$li_active_class. $aria_current.'>'. $tag .' '. single_tag_title( '' , false ). '</li>'; $str.='<li' .$li_active_class. $aria_current.'>'. single_tag_title( $tag , false ). '</li>'; //添付ファイルページ } elseif(is_attachment()){ $str.= $separator; $str.= '<li' .$li_active_class. $aria_current.'>'. wp_strip_all_tags($post->post_title) .'</li>'; //404 Not Found ページ } elseif(is_404()){ $str.= $separator; $str.='<li' .$li_active_class. $aria_current.'>'.$notfound.'</li>'; //その他 } else { $str.= $separator; $str.='<li' .$li_active_class. $aria_current.'>'. wp_get_document_title() .'</li>'; } $str.="\n".'</ul>'."\n"; $str.='</' .$nav_div .'>'."\n"; } echo $str; }
以下は上記パンくずリストのコードで使用している「一番下の階層のタームオブジェクトを返す関数」のコードで、上記コードを使用する場合はこちらも functions.php に記述する必要があります。
/** * 一番深い階層のタームオブジェクトを返す関数 * 2019/07/11 updated (不要な記述 $top_ancestor を削除) * 引数 $terms:(投稿が属する)タームオブジェクトの配列 * 引数 $mytaxonomy:タクソノミー名 * 引数 $myterm:優先するターム * 戻り値 $deepest:タームオブジェクト */ function get_deepest_term($terms, $mytaxonomy, $myterm = null){ global $post; if($myterm) { //$myterm が指定されていれば値からタームオブジェクトを生成 $my_pref_term = get_term_by( 'name', $myterm, $mytaxonomy ); //タームオブジェクトが取得できて且つそのタームが現在の投稿に属していれば if($my_pref_term && is_object_in_term( $post->ID, $mytaxonomy, $my_pref_term->term_id )) { //優先的にそのタームを返す return $deepest = $my_pref_term; } } //配列の要素が1つの場合その要素を最も深いタームとする if(count($terms) == 1 ){ $deepest = $terms[key($terms)]; }else{ $deepest = $terms[key($terms)]; //祖先オブジェクトの最大数の初期化 $max = 0; //それぞれのタームについて調査 for($i = 0; $i < count($terms); $i ++) { //上の階層から順番に取得した祖先オブジェクトの ID の配列 $ancestors = array_reverse(get_ancestors( $terms[$i]->term_id, $terms[$i]->taxonomy )); //祖先オブジェクトの数 $ancestors_count = count($ancestors); //祖先オブジェクトの数を比較して最大数より大きければ if($ancestors_count > $max) { //祖先オブジェクトの最大数を更新 $max = $ancestors_count; //その要素を最も深いタームとする $deepest = $terms[$i]; } } } return $deepest; }
以前掲載していたコードは「パンくずリストを作ってみるとWordPress でのサイト構築のコツがつかめるかもしれない /Web Design Recipes(現在は存在しないようです)」に掲載されていたコードを基にカスタム投稿タイプやカスタムタクソノミーにも対応するように変更したものでした。
以下は以前(2013年4月)に掲載していたコードからの変更・修正点です。
- タクソノミーとタームがごっちゃになっていたので変数名やコメントを修正
- div 要素でのマークアップから nav 要素へ変更(オプションで変更可能)
- 各要素のクラス名など属性のオプションを追加
- ホームやブログトップページの場合の出力をオプションで非表示に
- 投稿個別ページ及びカスタム投稿タイプ個別ページの場合で現在のページを出力しないオプションを追加
- カスタム投稿タイプ個別ページの場合アーカイブページを出力しないオプションを追加
- 一番下の階層のカテゴリーを取得する関数とタームを取得する関数を変更
- 上記に伴いカテゴリーとタームを同様に扱うため、カテゴリー名やカテゴリー ID はプロパティ name と term_id で取得(cat_name や cat_ID ではなく )
- カスタム投稿タイプでタクソノミーを使わない場合にも対応
- カスタム投稿タイプで通常のカテゴリやタグを使用した場合にも対応。
- カスタム投稿タイプで通常のカテゴリを表示させる場合はオプションを指定。但しその場合、カスタムタクソノミーを指定しても表示されない
- セパレータをオプションに変更
- オプションでカスタムフィールドを使って優先するカテゴリーやタクソノミーを選択可能に
- その他エスケープ処理の追加など
複数のカテゴリー(ターム)に属する投稿
デフォルトでは、投稿やカスタム投稿タイプの記事が複数のカテゴリーやタームに属している場合、どのカテゴリーやタームが表示されるかを選択することはできません。
基本的には複数のカテゴリーやタームに属している場合、カテゴリーやタームを表示する際はなるべく深い階層のカテゴリーやタームを表示するようにしています。
全て同じ階層の場合は、ID が若いものが選択されるはずです。
投稿やカスタム投稿タイプの記事が複数のカテゴリーやタームに属している場合に、どのカテゴリーやタームを表示するかは get_deepest_term() と言う関数で定義してあるので、こちらをカスタマイズすれば変更することが可能だと思います。
※オプションでカスタムフィールドを使って優先するカテゴリーやタームを選択可能にしました。(2019年07月10日)
使い方
my_breadcrumbs() は使用するには get_deepest_term() も functions.php にコピーしておく必要があります。
header.php などのパンくずリストを出力したい場所で関数を記述して呼び出します。関数名は任意の名前に変更できるので、変更した場合はその関数名で呼び出します。
以下はデフォルトで出力する例です(パラメータは全てオプションなので省略可能)。
<?php my_breadcrumbs(); ?>
例えば News と言う固定ページの場合、以下のようなパンくずリストが出力されます(見易いようにインデントしています)。
<nav> <ul> <li><a href="http://example.com/">Home</a></li> <li class="separator"> > </li><!-- セパレータ「 > 」の出力 --> <li>News</li> </ul> </nav>
例えば以下のように ul 要素に display:flex を指定すると、横並びのリストとして表示されます。
nav ul { list-style: none; display: flex; }
- Home
- >
- News
以下はオプションを指定する例です。
- nav 要素に breadcrumbs と言う id 属性を指定
- ホームページでは「Home」の文字を表示しない
- セパレータを「 / 」に変更(空白は で指定しています)
デフォルトのセパレータは class="separator" を指定した li 要素で「 > 」を出力します。
また、ファイルに HTML コードとして出力する際に改行するように前後に "\n" を追加しています(ブラウザに表示する際の改行ではありません)。
セパレータに空文字列 '' を指定すれば、セパレータの文字列及び li 要素を出力しません。
<?php my_breadcrumbs(array( 'id' => 'breadcrumbs', 'show_home' => false, 'separator' => "\n".'<li class="separator"> / </li>'."\n", )); ?>
デフォルトの値($defaults:コードの 37行目~58行目)を好みに合わせて変更すれば、パラメータを指定しないで済みます。
以下のようにパラメータを別途指定して、引数に指定しても同じです。
<?php $args = array( 'id' => 'breadcrumbs', 'aria_label' => 'Breadcrumb List', 'ul_class' =>'breadcrumb', 'aria_current' => 'page', ); my_breadcrumbs($args); ?>
例えば、上記のように指定すると固定ページでは以下のようなパンくずリストのマークアップが出力されます(見易いようにインデントしています)。
<nav id="breadcrumbs" aria-label="Breadcrumb List"> <ul class="breadcrumb"> <li><a href="http://example.com/">Home</a></li> <li class="separator"> > </li> <li aria-current="page">News</li> </ul> </nav>
Bootstrap のパンくずリスト
Bootstrap 4 のパンくずリストのスタイルを適用するには以下のようなマークアップで記述します。
<nav aria-label="パンくずリスト"> <ul class="breadcrumb"> <li class="breadcrumb-item"><a href="#">Home</a></li> <li class="breadcrumb-item"><a href="#">Library</a></li> <li class="breadcrumb-item active" aria-current="page">Data</li> </ul> </nav>
上記のようなマークアップでパンくずリストを出力するには、パラメータを以下のように指定します。
必要に応じて追加のオプションを指定します。
<?php $args = array( 'aria_label' => 'パンくずリスト', 'ul_class' =>'breadcrumb', 'li_class' => 'breadcrumb-item', 'li_active_class' => 'breadcrumb-item active', 'aria_current' => 'page', 'separator' => '', ); my_breadcrumbs($args); ?>
以下は階層のないカテゴリー(お知らせ)に属する投稿(sample 13)の場合に出力されるパンくずリストの表示とマークアップの例です。
<nav aria-label="パンくずリスト" > <ul class="breadcrumb" > <li class="breadcrumb-item" ><a href="http://example.com/">Home</a></li> <li class="breadcrumb-item" ><a href="http://example.com/category/news/">お知らせ</a></li> <li class="breadcrumb-item active" aria-current="page">sample 13</li> </ul> </nav>
Bootstrap のスタイルを適用するには、Bootstrap を読み込む必要があります。(WordPress での読み込み)
カテゴリーやタームの選択
デフォルトでは、投稿やカスタム投稿タイプの記事が複数のカテゴリーやタームに属している場合、どのカテゴリーやタームが表示されるかを選択することはできませんが、カスタムフィールドを利用することで選択することができます。
例えば、以下のようにカテゴリーを選択している場合、デフォルトでは階層の深い「エンパイア」と言うカテゴリーが表示されます。
もし「音楽」と言うカテゴリーを表示させたい場合は、カスタムフィールドの名前に myterm を指定して、値に表示したいカテゴリーの名前を指定します。
以下のように「音楽」を指定すると、デフォルトの「エンパイア」の代わりに「音楽」がパンくずリストに表示されます。
※ この指定は、JSON-LD で構造化マークアップを出力する関数 my_json_ld_breadcrumbs() にも反映されます。
また、例えば同じカテゴリーに属する親のカテゴリー名を表示したい場合は、親のカテゴリー名にチェックを入れて、カスタムフィールドの名前に myterm を指定して、値に親のカテゴリー名を指定します。
例えば、以下の場合、親カテゴリー「音楽」にチェックを入れても、デフォルトでは階層の深い「クラッシック」がパンくずリストに表示されます。
以下のように「音楽」を指定すると、デフォルトの「クラッシック」の代わりに親カテゴリーの「音楽」がパンくずリストに表示されます。
カスタム投稿タイプのタームでも同様にしてタームを選択することができます。
実際の運用では、独自にカスタムフィールドを追加するなどした方が使い勝手が良いと思います。
カスタムタクソノミーの選択
デフォルトではカスタム投稿タイプ等で複数のカスタムタクソノミーを登録して使用している場合、最初のカスタムタクソノミーを表示するようになっているため、それ以外のカスタムタクソノミーは表示できません。
個別のカスタムタイプ投稿の記事でカスタムフィールドを使って表示するカスタムタクソノミーを選択することができます。選択できるのは1つのタクソノミーだけです。
例えば、最初に登録されたカスタムタクソノミー(この例ではレンタルカテゴリー)ではなく後から追加したカスタムタクソノミーを表示する場合、以下のように編集画面でカスタムフィールドの名前に my_pref_tax を指定して、値に表示したいタクソノミーのラベル(この例ではレンタルテスト)を指定します。
※ この指定は、JSON-LD で構造化マークアップを出力する関数 my_json_ld_breadcrumbs() にも反映されます。
コードの概要
テンプレート階層を考慮して以下のような条件分岐タグを使ってページの種類によってパンくずリストを生成しています。
分岐するページの種類 | 条件分岐タグ | パンくずリストコード補足説明等 |
---|---|---|
フロントページ | is_front_page() || is_home() | 「管理画面」→「設定」→「表示設定」の「ホームページの表示」での設定により異なります |
タクソノミーページ | is_tax() | タクソノミーの属するカスタム投稿タイプ名を表示してその後にタクソノミーを階層的に表示 |
カテゴリーページ | is_category() | カテゴリーを階層的に表示 |
カスタム投稿タイプアーカイブページ | is_post_type_archive() | カスタム投稿タイプ名を表示 |
カスタム投稿タイプ個別ページ | is_singular($cpt) | 投稿に属するタクソノミーやカテゴリーがあれば階層的に表示 |
個別投稿ページ | is_single() && !is_attachment() | 添付ファイルを除外。属するカテゴリーを階層的に表示 |
固定ページ | is_page() | 親ページがあれば階層的に表示 |
日付別アーカイブページ | is_date() | get_query_var で取得できる値で日別、月別、年別に更に分岐 |
検索結果ページ | is_search() | デフォルトでは「xxxx で検索した結果」のように get_search_query() で取得したキーワード(xxxx)を使って表示 |
投稿者別アーカイブページ | is_author() | 投稿者名を表示 |
タグアーカイブページ | is_tag() | デフォルトでは「タグ : タグ名」と表示 |
添付ファイルページ | is_attachment() | 添付ファイルのタイトルを表示 |
404 ページ | is_404() | デフォルトでは「404 Not found」と表示 |
その他 | 上記分岐で該当なし | wp_get_document_title でページのタイトルを取得して表示 |
コードの内容はコード内のコメントを参照ください。
関連ページ:条件分岐タグ
利用した関数
その他に以下のような関数を使っています。
関数名 | 説明 |
---|---|
wp_parse_args | ユーザーの指定した引数とデフォルトの値をマージ |
extract | 連想配列を変数として抽出する PHP の関数 |
array_reverse | 要素を逆順にした配列を返す PHP の関数 |
home_url | 現在のブログのホーム URL を取得 |
get_month_link | 指定した年・月の月別アーカイブページの URL を取得 |
get_query_var | query_vars から指定したキーの値を取得 |
get_queried_object | 現在表示しているページのオブジェクトを取得 |
get_the_ID | 現在の投稿の ID を取得 |
get_the_category | 投稿が属するカテゴリー(オブジェクト)を取得 |
get_category_link | 指定したカテゴリーのアーカイブページの URL を取得 |
get_cat_name | カテゴリー ID からカテゴリーの名前を取得 |
get_category | 指定したカテゴリーオブジェクトを取得 |
get_taxonomy | 指定したタクソノミーオブジェクトを取得 |
get_taxonomies | 登録されているタクソノミーの名前またはオブジェクトの配列を取得 |
get_object_taxonomies | 指定された投稿タイプに登録されている全てのタクソノミーオブジェクト(または名前)を取得 |
get_post_type_archive_link | 指定した投稿タイプのアーカイブページのリンクを取得 |
get_post_type_object | 投稿タイプ(を表す)オブジェクトを取得 |
get_the_terms | 投稿に割り当てられたタクソノミーのタームオブジェクトを取得 |
get_term_link | 指定されたタームのアーカイブページへの URL を取得 |
get_term | ターム ID を指定してタームオブジェクトを取得 |
get_term_by | タームの名前やスラッグからタームオブジェクトを取得 |
single_tag_title | タグのアーカイブでタグ名(タイトル)を出力 |
get_tag_link | 指定したタグのアーカイブページの URL を取得(my_json_ld_breadcrumbs() で使用) |
get_search_query | 検索フォームで入力された検索キーワードを取得 |
wp_get_document_title | 現在のページのタイトルを取得 |
esc_url | URL のプロトコルのチェックや適切でない文字をエスケープして URL を無害化 |
wp_strip_all_tags | 指定された文字列からあらゆる HTML タグを除去。PHP の strip_tags() でも可能 |
get_post_meta | 投稿 ID とカスタムフィールドの名前を指定してカスタムフィールドの値を取得 |
is_object_in_term() | 指定された投稿などのオブジェクトが指定されたタームの何れかに関連付けられているかを判定 |
is_object_in_taxonomy() | 指定された投稿タイプ(オブジェクトタイプ)が指定されたタクソノミーに関連付けられているかを判定 |
get_term_children | 指定されたタームの子タームを全て取得 |
get_ancestors | 指定したオブジェクトの祖先オブジェクトの ID の配列を取得 |
get_term_children
指定されたタームの子タームを全て取得して1つの配列に入れて返します。
階層のあるタクソノミーについてのみ有用で、もし指定されたタクソノミーにタームが無ければ空の配列を返します。
get_term_children( $term, $taxonomy )
- パラメータ
-
- $term(文字列|整数):(必須)子タームを取得するタームの ID
- $taxonomy(文字列):(必須)タクソノミーの名前
- 戻り値
- ターム ID の配列。タクソノミーが存在しなければ WP_Error
以下は rental_cat と言うカスタムタクソノミーの small と言うタームの子タームを取得して、get_term_link() でリンクを付けて出力する例です。
get_term_children() にはタームの ID を指定する必要があるので、get_term_by() を使って取得したタームオブジェクトから ID を取得しています。
<?php $term_slug = 'small'; $taxonomy_name = 'rental_cat'; $term_id = get_term_by( 'slug', $term_slug , $taxonomy_name ) -> term_id; $termchildren = get_term_children( $term_id, $taxonomy_name ); if(is_array($termchildren)) { echo '<ul>'; foreach ( $termchildren as $child ) { $term_obj = get_term_by( 'id', $child, $taxonomy_name ); echo '<li><a href="' . esc_url( get_term_link( $child, $taxonomy_name ) ). '">' . $term_obj->name . '</a></li>'; } echo '</ul>'; } ?>
以下は taxonomy.php などのカスタムタクソノミーのアーカイブページのテンプレートで、そのページのタームに子タームがあればリンクを付けて出力する例です。
taxonomy.php などのカスタムタクソノミーのアーカイブページの場合、$term と $taxonomy と言う変数に自動的にそのページのタームとタクソノミーが入っているので前述の例に比べて簡潔に記述できます。
<?php $term_id = get_term_by( 'slug', $term , $taxonomy ) -> term_id; $termchildren = get_term_children( $term_id, $taxonomy ); if(is_array($termchildren)) { echo '<ul>'; foreach ( $termchildren as $child ) { $term_obj = get_term_by( 'id', $child, $taxonomy ); echo '<li><a href="' . esc_url( get_term_link( $child, $taxonomy ) ). '">' . $term_obj->name . '</a></li>'; } echo '</ul>'; } ?>
以下は実用的なサンプルではありませんが、カスタム投稿タイプの個別ページでその投稿の属するタームを get_the_terms() で取得し、そのタームに子タームがあればリンクを付けて出力する例です。
<?php $taxonomy_name = 'rental_cat'; //カスタムタクソノミー名 $terms = get_the_terms($post->ID, $taxonomy_name); if ( $terms && ! is_wp_error( $terms ) ) { foreach ( $terms as $term ) { $termchildren = get_term_children( $term -> term_id, $taxonomy_name ); if(is_array($termchildren)) { echo '<ul>'; foreach ( $termchildren as $child ) { $term = get_term_by( 'id', $child, $taxonomy_name ); echo '<li><a href="' . esc_url(get_term_link( $child, $taxonomy_name ) ). '">' . $term->name . '</a></li>'; } echo '</ul>'; } } } ?>
以下は get_term_children() のソースです。
function get_term_children( $term_id, $taxonomy ) { if ( ! taxonomy_exists( $taxonomy ) ) { return new WP_Error( 'invalid_taxonomy', __( 'Invalid taxonomy.' ) ); } $term_id = intval( $term_id ); $terms = _get_term_hierarchy( $taxonomy ); if ( ! isset( $terms[ $term_id ] ) ) { return array(); } $children = $terms[ $term_id ]; foreach ( (array) $terms[ $term_id ] as $child ) { if ( $term_id == $child ) { continue; } if ( isset( $terms[ $child ] ) ) { $children = array_merge( $children, get_term_children( $child, $taxonomy ) ); } } return $children; }
get_ancestors
指定したオブジェクトの祖先オブジェクトの ID の配列を返します。
get_ancestors( $object_id, $object_type, $resource_type )
- パラメータ
-
- $object_id(整数):(オプション)オブジェクトの ID。オプションだが指定する必要あり。
- $object_type(文字列):(オプション)祖先オブジェクトの種類。page や category などの(階層を持った)投稿タイプまたはタクソノミー名を指定。オプションだが指定する必要あり。
- $resource_type(文字列):(オプション)$object_type の種類。'post_type' または 'taxonomy' を指定。省略可能。
- 戻り値
- 祖先オブジェクトの ID の配列(階層の下から上の順)。指定したオブジェクトが親を持たない、または存在しない場合は空の配列。
以下は ID が 42 のカテゴリーの祖先オブジェクトの ID を取得し var_dump() で確認する例です。
<?php $cat_ancestors = get_ancestors( 42, 'category' ); var_dump($cat_ancestors); ?>
祖先オブジェクトが存在すれば、例えば以下のような出力になります。以下は2つの祖先オブジェクトがある例です。
array(2) { [0]=>int(35) //祖先オブジェクトの ID [1]=>int(41) //祖先オブジェクトの ID }
以下は get_cat_ID() でカテゴリー名から ID を取得して祖先オブジェクトの ID を取得し、get_category() でオブジェクトに変換して名前と ID を表示する例です。
<?php $cat_ancestors = get_ancestors( get_cat_ID('クラッシック'), 'category' ); foreach($cat_ancestors as $cat_ancestor) { $cat = get_category($cat_ancestor); echo 'カテゴリー名: ' . $cat ->name . ' ID: ' . $cat -> term_id . '<br>'; } ?> //出力例 //カテゴリー名: 音楽 ID: 35 //カテゴリー名: 趣味 ID: 41
以下は個別ページのテンプレートで投稿に属するカテゴリーを get_the_category() を使って取得して、祖先カテゴリーがあればそのリンクを get_category_link() で出力する例です。
<?php //投稿が属するカテゴリーオブジェクトを取得 $cats = get_the_category(); //重複した値を確認するための配列 $cats_array = []; foreach($cats as $cat) { $cat_ancestors = get_ancestors( $cat->term_id, 'category' ); //それぞれの祖先オブジェクトを取得してリンクを出力 foreach($cat_ancestors as $cat_ancestor) { $cat = get_category($cat_ancestor); if($cat) { //同じリンクがすでに出力されているかを配列の値で確認 if(!array_search($cat->name, $cats_array)) { $cats_array[] = $cat->name; echo '<a href="'. esc_url( get_category_link( $cat->term_id ) ) .'">'. $cat->name .'</a> '; } } } } ?>
以下は ID が 1249 の固定ページの祖先オブジェクトの ID を取得し var_dump() で確認する例です。
<?php $page_ancestors = get_ancestors( 1249, 'page' ); var_dump($page_ancestors); ?>
以下は1つの祖先オブジェクトが存在する場合の出力例です
array(1) { [0]=>int(150) //祖先オブジェクトの ID }
以下は get_ancestors() のソースです。
function get_ancestors( $object_id = 0, $object_type = '', $resource_type = '' ) { $object_id = (int) $object_id; $ancestors = array(); if ( empty( $object_id ) ) { /** This filter is documented in wp-includes/taxonomy.php */ return apply_filters( 'get_ancestors', $ancestors, $object_id, $object_type, $resource_type ); } if ( ! $resource_type ) { if ( is_taxonomy_hierarchical( $object_type ) ) { $resource_type = 'taxonomy'; } elseif ( post_type_exists( $object_type ) ) { $resource_type = 'post_type'; } } if ( 'taxonomy' === $resource_type ) { $term = get_term( $object_id, $object_type ); while ( ! is_wp_error( $term ) && ! empty( $term->parent ) && ! in_array( $term->parent, $ancestors ) ) { $ancestors[] = (int) $term->parent; $term = get_term( $term->parent, $object_type ); } } elseif ( 'post_type' === $resource_type ) { $ancestors = get_post_ancestors( $object_id ); } /** * Filters a given object's ancestors. * @since 3.1.0 * @since 4.1.1 Introduced the `$resource_type` parameter. * @param array $ancestors An array of object ancestors. * @param int $object_id Object ID. * @param string $object_type Type of object. * @param string $resource_type Type of resource $object_type is. */ return apply_filters( 'get_ancestors', $ancestors, $object_id, $object_type, $resource_type ); }
JSON-LD で構造化マークアップ
対象としているページの種類は以下になります。関数を呼び出すフィルターに条件分岐タグを使えば、対象のページを限定することが可能です(条件分岐タグで出力するページを限定)。
表示する項目のオプションはパンくずリストの関数とほぼ同じです。パンくずリストの関数と一緒に使う場合は、オプションを合わせるようにすると良いかと思います。
- ホーム(オプションで対象外に指定可能)
- カスタムタクソノミーアーカイブページ
- カテゴリーアーカイブページ
- カスタム投稿タイプアーカイブページ
- カスタム投稿タイプ個別記事ページ
- 個別投稿ページ
- 固定ページ
- タグのアーカイブページ
以下がコードで、functions.php に記述します。
/*** パンくずリスト JSON-LD 構造化マークアップ my_json_ld_breadcrumbs 2019/07/14 updated *** * 2019/07/14 カスタム投稿タイプにカスタム分類が登録されていない場合に対応(183行目追加) * * [注意]この関数は別途 get_deepest_term() が必要です。 * * カスタムフィールド myterm を使って優先するカテゴリーやタームを指定可能に 2019/07/10 * * パラメータ $args :引数の連想配列(全てオプション)。以下概要。 * show_home ホームの場合にホームの構造化マークアップを出力するかどうか。初期値 true(出力する) * show_cpta カスタム投稿タイプ個別ページでアーカイブページを出力するかどうか。 初期値 true(出力する)2019/07/06 Added * home ホームの文字列。初期値:Home * blog_home 管理画面のホームページの表示で「固定ページ」→「投稿ページ(メインブログページ)」に * 指定したページで出力する文字。初期値:Blog * cat_off 個別ページでカテゴリーを出力しない。初期値: false(出力する) * cat_parents_off 個別ページで親のカテゴリーを表示しない。初期値: false(表示する) * tax_off 個別ページでタクソノミー(ターム)を出力しない。初期値:false(出力する) * tax_parents_off カスタム投稿タイプ個別ページで親のタームを表示しない。初期値:false(表示する) * extra_top_id 先頭(ホームの前)に更に階層を追加する場合に"name"に出力する名前を指定。 初期値:''(追加しない) * extra_top_url 先頭(ホームの前)に更に階層を追加する場合に"@id"に出力する URL を指定。 初期値:''(追加しない) * show_cat_tag_for_cpt カスタム投稿タイプ個別ページでカテゴリーを表示する(その場合は、カスタムタクソノミーは表示されない)。初期値:false(表示しない) * show_terms カスタム投稿タイプでタームを表示 * 記事が複数のカテゴリーやタームに属する場合、カスタムフィールド(myterm)を使って優先するカテゴリーやタームを指定可能 * カスタム投稿タイプに複数のカスタムタクソノミーが登録されている場合、カスタムフィールド(my_pref_tax)を使ってタクソノミーを指定可能 */ function my_json_ld_breadcrumbs($args = array()){ global $post; // デフォルトの値 $defaults = array( 'show_home' => true, 'show_cpta' => true, 'home' => 'Home', 'blog_home' => 'Blog', 'cat_off' => false, 'cat_parents_off' => false, 'tax_off' => false, 'tax_parents_off' => false, 'extra_top_id' => '', 'extra_top_url' => '', 'show_cat_tag_for_cpt' => false, 'show_terms' => false, ); //引数の値とデフォルトをマージ $args = wp_parse_args( $args, $defaults ); extract( $args, EXTR_SKIP ); //出力文字列の初期化 $str ='{ "@context": "http://schema.org", "@type": "BreadcrumbList", "itemListElement": ['."\n"; //position カウンター $count = 1; //先頭(ホームの前)に追加するマークアップ $extra_top_str = ''; //先頭(ホームの前)に追加する場合 if($extra_top_id && $extra_top_url) { $extra_top_str = '{"@type": "ListItem", "position": '.$count.', "item":{ "@id":"'. $extra_top_url. '", "name":"'.$extra_top_id. '"}},' ."\n"; $str.= $extra_top_str; $count ++; } //Home 文字列 $home_str = '{"@type": "ListItem", "position": '.$count.', "item":{ "@id":"'. home_url(). '", "name":"'.$home. '"}},' ."\n"; //ホーム・フロントページの場合 if(is_front_page() || is_home()) { if($show_home) { $label = is_front_page() ? $home: $blog_home; $str.= '{"@type": "ListItem", "position": '.$count.', "item":{'; $str.= '"@id":"'. home_url(). '", "name":"'.$label. '"}}'."\n"; }else{ return ''; } } //ホーム・フロントページでない場合(且つ管理ページでない場合) if(!is_front_page() && !is_home() && !is_admin()){ $str.= $home_str; //タクソノミー名を取得(タクソノミーアーカイブの場合のみ取得可能) $my_taxonomy = get_query_var('taxonomy'); //投稿タイプ名を取得(カスタム投稿タイプ個別ページの場合のみ取得可能) $cpt = get_query_var('post_type'); //カスタムタクソノミーアーカイブページ if($my_taxonomy && is_tax($my_taxonomy)) { //タームオブジェクト(現在のページのオブジェクト)を取得 $my_term = get_queried_object(); //タクソノミーオブジェクトの object_type プロパティは配列 $post_types = get_taxonomy( $my_taxonomy )->object_type; //配列の0番目からカスタム投稿タイプのスラッグ(カスタム投稿タイプ名)を取得 $cpt = $post_types[0]; //カスタム投稿タイプのアーカイブページを追加 $count++; $str.= '{"@type": "ListItem", "position": '.$count.', "item":{ "@id":"'. esc_url(get_post_type_archive_link($cpt)). '", "name":"'.get_post_type_object($cpt)->label. '"}},'."\n"; //タームオブジェクトに親があればそれらを取得して追加 if($my_term->parent != 0) { //祖先タームオブジェクトの ID の配列を取得し逆順に(取得される配列の並びは階層の下から上) $ancestors = array_reverse(get_ancestors( $my_term->term_id, $my_term->taxonomy )); //全ての祖先タームオブジェクトのアーカイブページを追加 foreach($ancestors as $ancestor){ $count++; $str.= '{"@type": "ListItem", "position": '.$count.', "item":{ "@id":"'. esc_url(get_term_link($ancestor, $my_term->taxonomy)). '", "name":"'. get_term($ancestor, $my_term->taxonomy)->name. '"}},'."\n"; } } //タームを追加 $count++; $str.= '{"@type": "ListItem", "position": '.$count.', "item":{ "@id":"'. esc_url(get_term_link($my_term, $my_term->taxonomy)). '", "name":"'. get_term($my_term, $my_term->taxonomy)->name. '"}}'."\n"; //カテゴリーのアーカイブページ }elseif(is_category()) { //カテゴリーオブジェクトを取得 $cat = get_queried_object(); //取得したカテゴリーオブジェクトに親があればそれらを取得して追加 if($cat->parent != 0){ $ancestors = array_reverse(get_ancestors( $cat->term_id, 'category' )); foreach($ancestors as $ancestor){ $count++; $str.= '{"@type": "ListItem", "position": '.$count.', "item":{ "@id":"'. esc_url(get_category_link($ancestor)). '", "name":"'. get_cat_name($ancestor). '"}},'."\n"; } } //テゴリー名を追加 $count++; $str.= '{"@type": "ListItem", "position": '.$count.', "item":{ "@id":"'. esc_url(get_category_link($cat)). '", "name":"'. $cat->name. '"}}'."\n"; //カスタム投稿のアーカイブページ } elseif(is_post_type_archive()) { //カスタム投稿タイプ名を取得 $cpt = get_query_var('post_type'); //カスタム投稿タイプを追加 $count++; $str.= '{"@type": "ListItem", "position": '.$count.', "item":{ "@id":"'. esc_url(get_post_type_archive_link($cpt)). '", "name":"'. get_post_type_object($cpt)->label. '"}}'."\n"; //カスタム投稿タイプの個別記事ページ } elseif($cpt && is_singular($cpt)){ if($show_cpta) { //カスタム投稿タイプアーカイブページを追加 $count++; $str.= '{"@type": "ListItem", "position": '.$count.', "item":{ "@id":"'. esc_url(get_post_type_archive_link($cpt)). '", "name":"'.get_post_type_object($cpt)->label. '"}},'."\n"; } //このカスタム投稿タイプに登録されている全てのタクソノミーオブジェクトの名前を取得 $taxes = get_object_taxonomies( $cpt ); //タクソノミーオブジェクトの名前が取得できれば if(count($taxes) !== 0) { //タクソノミーを表示する場合 if(!$tax_off) { //配列の先頭のタクソノミーオブジェクトの名前(複数ある可能性があるので先頭のものを使う) //デフォルトでは標準のカテゴリーやタグが追加されている場合はインデックスを変更 //但し、show_cat_tag_for_cpt が true の場合はカテゴリーを取得可能に $tax_index = 0; if(!$show_cat_tag_for_cpt) { for ($i = 0; $i < count($taxes); $i++) { if($taxes[$i] !== 'category' && $taxes[$i] !== 'post_tag' && $taxes[$i] !== 'post_format') { $tax_index = $i; break; } } } $mytax = $taxes[$tax_index]; //カスタムフィールドに優先するタクソノミーのラベルが記載されていればそのタクソノミーを選択 //タクソノミーのラベルを取得 $my_pref_tax_label = get_post_meta( get_the_ID(), 'my_pref_tax', true) ? esc_attr(get_post_meta( get_the_ID(), 'my_pref_tax', true)) : null; //ラベルからタクソノミーを取得(戻り値はタクソノミーの名前の配列) $my_pref_tax_name = get_taxonomies(array('label'=> $my_pref_tax_label)); //タクソノミー名の初期化 $my_pref_tax = ''; //取得した配列が1つの場合、その値が優先されるタクソノミーの名前 if(count($my_pref_tax_name) == 1 ){ $my_pref_tax = $my_pref_tax_name[key($my_pref_tax_name)]; } //タクソノミーの名前が取得できて且つそのタクソノミーが現在の投稿タイプに属している場合は、そのタクソノミーを使用 if($my_pref_tax && is_object_in_taxonomy($post->post_type, $my_pref_tax)) { $mytax = $my_pref_tax; } //投稿に割り当てられたタームオブジェクト(配列)を取得 $terms = get_the_terms($post->ID, $mytax); //カスタムフィールドに優先するタームが記載されていればその値を取得して $myterm へ $myterm = get_post_meta( get_the_ID(), 'myterm', true) ? esc_attr(get_post_meta( get_the_ID(), 'myterm', true)) : null; //$terms が取得できていれば一番下の階層のタームを取得(できない場合は null に) $my_term = $terms ? get_deepest_term($terms, $mytax, $myterm) : null; //タームが取得できていれば if( !empty($my_term) ) { //タームに親があればそれらを取得してリンクを生成してリストに追加 if($my_term->parent != 0 && !$tax_parents_off){ $ancestors = array_reverse(get_ancestors( $my_term->term_id, $mytax )); foreach($ancestors as $ancestor){ $count++; $str.= '{"@type": "ListItem", "position": '.$count.', "item":{ "@id":"'. esc_url(get_term_link($ancestor, $mytax)). '", "name":"'.get_term($ancestor, $mytax)->name. '"}},'."\n"; } } if($show_terms) { //タームを追加(不要?) $count++; $str.= '{"@type": "ListItem", "position": '.$count.', "item":{ "@id":"'. esc_url(get_term_link($my_term, $mytax)). '", "name":"'.$my_term->name. '"}},'."\n"; } } $count++; $str.= '{"@type": "ListItem", "position": '.$count.', "item":{ "@id":"'. esc_url(get_permalink($post->post_id)). '", "name":"'.wp_strip_all_tags($post->post_title). '"}}'."\n"; } }else{ //タクソノミーを表示しない場合 $count++; $str.= '{"@type": "ListItem", "position": '.$count.', "item":{ "@id":"'. esc_url(get_permalink($post->post_id)). '", "name":"'.wp_strip_all_tags($post->post_title). '"}}'."\n"; } //個別投稿ページ(添付ファイルも true と判定されるので除外) }elseif(is_single() && !is_attachment()){ //投稿が属するカテゴリーオブジェクトの配列を取得 $categories = get_the_category($post->ID); //カテゴリーを表示する場合 if(!$cat_off) { //カスタムフィールドに優先するカテゴリーが記載されていればその値を取得して $myterm へ $myterm = get_post_meta( get_the_ID(), 'myterm', true) ? esc_attr(get_post_meta( get_the_ID(), 'myterm', true)) : null; //一番下の階層のカテゴリーを取得 $cat = get_deepest_term($categories, 'category', $myterm); //カテゴリーに親があればそれらを取得して追加 if($cat->parent != 0 && !$cat_parents_off){ $ancestors = array_reverse(get_ancestors( $cat->term_id, 'category' )); foreach($ancestors as $ancestor){ $count++; $str.= '{"@type": "ListItem", "position": '.$count.', "item":{ "@id":"'. esc_url(get_category_link($ancestor)). '", "name":"'.get_cat_name($ancestor). '"}},'."\n"; } } //カテゴリーを追加 $count++; $str.= '{"@type": "ListItem", "position": '.$count.', "item":{ "@id":"'. esc_url(get_category_link($cat->term_id)). '", "name":"'.$cat->name. '"}},'."\n"; } $count++; $str.= '{"@type": "ListItem", "position": '.$count.', "item":{ "@id":"'. esc_url(get_permalink($post->post_id)). '", "name":"'.wp_strip_all_tags($post->post_title). '"}}'."\n"; //固定ページ } elseif(is_page()){ //固定ページに親があればそれらを取得してリンクを生成してリストに追加 if($post -> post_parent != 0 ){ $ancestors = array_reverse(get_post_ancestors( $post->ID )); foreach($ancestors as $ancestor){ $count++; $str.= '{"@type": "ListItem", "position": '.$count.', "item":{ "@id":"'. esc_url(get_permalink($ancestor)). '", "name":"'. wp_strip_all_tags(get_the_title($ancestor)). '"}},'."\n"; } } //固定ページを追加 $count++; $str.= '{"@type": "ListItem", "position": '.$count.', "item":{ "@id":"'. esc_url(get_permalink($post->post_id)). '", "name":"'. wp_strip_all_tags($post->post_title). '"}}'."\n"; //タグのアーカイブページ } elseif(is_tag()){ //タームオブジェクトを取得 $tag = get_queried_object(); $count++; $str.= '{"@type": "ListItem", "position": '.$count.', "item":{ "@id":"'. esc_url( get_tag_link($tag->term_id) ). '", "name":"'. single_tag_title( '' , false ). '"}}'."\n"; } else { return ''; } } $str.= "\n".']'."\n".'}'."\n"; return '<script type="application/ld+json">' ."\n". $str . '</script>'."\n"; }
上記コードはパンくずリストのコードを基に、対象のページを限定してリンクの代わりに構造化マークアップを出力していて内容的にはほぼ同じです。
使い方
my_json_ld_breadcrumbs() を使用するには get_deepest_term() も functions.php にコピーしておく必要があります。
以下のように記述すると構造化マークアップを </body> 閉じタグの直前に出力します。
add_action( 'wp_footer', 'add_my_json_ld_breadcrumbs', 999999 ); function add_my_json_ld_breadcrumbs() { if ( function_exists( 'my_json_ld_breadcrumbs' ) ) { echo my_json_ld_breadcrumbs(); } }
以下はパラメータの show_home と cat_off を指定する例です。
以下の場合、ホームでは構造化マークアップを出力せず、投稿個別ページではカテゴリーを出力しません。
add_action( 'wp_footer', 'add_my_json_ld_breadcrumbs', 100 ); function add_my_json_ld_breadcrumbs() { if ( function_exists( 'my_json_ld_breadcrumbs' ) ) { echo my_json_ld_breadcrumbs(array( 'show_home'=>false, 'cat_off'=>true )); } }
マークアップの出力場所
add_action() の第1パラメータに wp_footer を指定するとフッター( </body> 閉じタグの前)に、wp_head を指定するとヘッダー(<head> 要素内)にマークアップを出力します。
フッターやヘッダー内での出力位置は add_action() の第3パラメータの優先度(整数値)で調整できます。
ヘッダー内に出力する場合は、以下のように add_action() の第1パラメータのアクションに wp_head を指定します。
add_action( 'wp_head', 'add_my_json_ld_breadcrumbs', 10 ); function add_my_json_ld_breadcrumbs() { if ( function_exists( 'my_json_ld_breadcrumbs' ) ) { echo my_json_ld_breadcrumbs(); } }
条件分岐タグで出力するページを限定
以下はカテゴリーやカスタムタクソノミータームのアーカイブページでは構造化マークアップを出力しない例です。
必要であれば、条件分岐を使ってページ種類によって異なるパラメータを指定することができます。
add_action( 'wp_head', 'add_my_json_ld_breadcrumbs', 10 ); function add_my_json_ld_breadcrumbs() { if ( function_exists( 'my_json_ld_breadcrumbs' ) ) { //条件分岐タグで関数の実行を限定(カテゴリーやタームのアーカイブページでなければ実行) if( !(is_category() || is_tax()) ) { echo my_json_ld_breadcrumbs(); } } }
トップの階層に追加
以下は WordPress をサブディレクトリにインストールしている場合などで、トップの階層に更に上の階層のマークアップを追加する例です。
add_action( 'wp_footer', 'add_my_json_ld_breadcrumbs', 999999 ); function add_my_json_ld_breadcrumbs() { if ( function_exists( 'my_json_ld_breadcrumbs' ) ) { echo my_json_ld_breadcrumbs(array( 'extra_top_id'=>'Home', 'extra_top_url' => 'https://www.webdesignleaves.com', 'home'=> 'Web制作メモ' )); } }
上記は WordPress を wp と言うディレクトリにインストールしていて、追加したトップのマークアップの name を Home に、@id を https://www.webdesignleaves.com に、home の文字列を「Web制作メモ」にする例です。
「構造化データテストツール」で確認すると以下のような結果になります。
関連ページ:SEO /パンくずリスト/構造化マークアップ