WordPressに慣れていないと投稿をカスタマイズするので大変なのに、カスタム投稿タイプだとさらに大変ですし、混乱します。
おおまかな構築の流れさえ理解していれば、似たようなことをやっていることに気づきます。
その構築の流れをざっくり紹介していきます。
前回は、カスタム投稿タイプ・カスタムタクソノミーの作成、詳細ページの構築について紹介しました。
【WordPressカスタム投稿】記事一覧・詳細を実装してみよう(前編)
今回は後編として、カスタム投稿タイプの一覧ページ + α(タクソノミー一覧、トップへの表示など) 解説してます。
目次
テンプレートファイルの配置場所
これからいくつかテンプレートファイルを作成していきます。
テンプレートファイルは、WordPressのページを生成するために使われるPHPファイルです。
HTML、PHP、そして WordPressテンプレートタグで構成されています。
ヘッダーやフッターなど共通化できる部分は、そこだけを記述したテンプレートファイルを作成します。このような共通したパーツをテンプレートパーツと呼びます。
テンプレートパーツの具体例としてheader.phpやfooter.phpが挙げられます。
作成するテンプレートファイルの配置する場所を図で示しました。
作成する場所を間違えないように気を付けて下さい。
一覧ページの実装
一覧ページはターム一覧の表示、ページネーションなど詰まるポイントがたくさんあるなと思っています。
ひとまず、記事の一覧を表示させていきましょう。
テンプレートはarchive-カスタム投稿タイプ名.phpです。今回だとarchive-news.phpになります。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 | <?php get_header(); ?> <div class="page-news"> <main class="news-archive"> <?php if ( have_posts() ) : ?> <?php while ( have_posts() ) : the_post(); $thumbnail = ( has_post_thumbnail() ) ? get_the_post_thumbnail_url( get_the_ID(), 'medium' ) : 'https://placehold.jp/150x150.png'; ?> <article class="news-item"> <a href="<?php the_permalink(); ?>" class="news-item__inner"> <div class="news-item__media js-img-bg"> <img src="<?php print $thumbnail; ?>" alt=""> </div> <div class="news-item__body"> <h2 class="news-item__title"><?php the_title(); ?></h2> <time datetime="the_time( 'Y-m-d' )"><?php the_time( 'Y.m.d' ); ?></time> <p class="news-item__text"><?php print mb_strimwidth( strip_tags( get_the_content() ), 0, 200, "…", "UTF-8" ); ?></p> </div> </a> </article> <?php endwhile; ?> <?php else : ?> 何も投稿がありません。 <?php endif; ?> </main> </div> <?php get_footer(); ?> |
その後、管理画面の左のメニュー「お知らせ」をクリックし、投稿一覧を表示をクリックすると
このように表示されると思います。
コードを解説する前に、ループについて話します。
ループについて
WordPressでは、以下の形が頻出します。
1 2 3 4 5 6 7 8 9 10 11 | <?php if ( have_posts() ) : ?> <?php while ( have_posts() ) : the_post(); ?> <?php endwhile; ?> <?php endif; ?> |
ページに投稿を表示するための部分で、ループと言います。
ループにはメインループとサブループがあって、ここではメインループが行われています。
仮に、「http://example.jp/news/」というURLを見たいとします。
静的ページだと、サーバーにあるnewsディレクトリのindex.htmlを読み込みます。
それに対してWordPressは、
①リクエストされたURLを元に、データベースに投稿データの取得を要求し、取得する(メインクエリ)
②メインクエリをグローバル変数$wp_queryに格納
③テンプレート階層に従って、テンプレートが選択される
④テンプレート内でループが実行され、記事が出力される
のような流れでページが表示されています。
これがURLリクエストされると自動的に行われています。このメインクエリによるループがメインループです。
メインクエリの表示順や、特定の投稿を除外するにはクエリの条件を変更する必要があります。
pre_get_posts()というアクションフックを使って、投稿を取得する前にクエリ条件を変更することができます。
例えば、管理画面の設定 > 表示設定で1ページに表示する最大投稿数が10件になっているけど、特定のカスタム投稿タイプでは20件まで表示したい場合
1 2 3 4 5 6 7 8 9 | add_action('pre_get_posts', 'my_pre_get_posts'); function my_pre_get_posts($query) { if (!is_admin() && $query->is_main_query() && is_post_type_archive('カスタム投稿タイプ名')) { $query->set('posts_per_page', 20); } } |
とfunctions.phpに記述することで一覧ページが最大20件まで表示することができます。
ほかにも投稿の古い順に表示するなど指定することができます。
複雑な条件の場合やリクエストしたURLに依らない投稿の情報を取得したい場合は、自分で条件を指定して、ベータベースに要求して取得します。これをサブクエリといい、サブクエリを出力するループをサブループといいます。
コードの解説
今回はお知らせ一覧を取得したいだけなのでメインクエリを回してます。
記事の表示のさせ方は詳細ページとほぼ同じなので、省略しますね。
アイキャッチ画像表示するコードは少し解説します。
1 2 3 4 | $thumbnail = ( has_post_thumbnail() ) ? get_the_post_thumbnail_url( get_the_ID(), 'medium' ) : 'https://placehold.jp/150x150.png'; |
こちらのコードですね。has_post_thumbnail()でアイキャッチが設定されているか判定して、設定されていたらget_the_post_thumbnail_url()でアイキャッチ画像のURLを文字列として取得しています。設定されていない場合は、ダミー画像が表示されるようにしました。
1 2 3 4 5 6 | get_the_post_thumbnail_url( $post, $size); // $post : 投稿IDまたはWP_Postオブジェクト // $size : ソースを取得するための登録済み画像サイズ |
$sizeを指定するとそのサイズの画像URLが取得できます。画像の大きさと$sizeの大きさが違うともともとの画像のURLが取得されます。
アイキャッチが設定されていない場合、画像自体を表示させないのかダミー画像を表示させるか確認した方がいいです。ダミー画像を表示させる場合は、デザイナーの人に作成を依頼する必要があります。
ページネーションの実装
このままだと、1ページに表示する最大投稿数より多く投稿すると記事が見えなくなってしまいます。
なのでページネーションを実装しましょう。
archive-news.phpに記述してもいいのですが、見やすさのためテンプレートパーツに分割しています。
archive-news.phpに以下のようにコードを追加します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 | <?php get_header(); ?> <div class="page-news"> <main class="news-archive"> <?php if ( have_posts() ) : ?> <?php while ( have_posts() ) : the_post(); $thumbnail = ( has_post_thumbnail() ) ? get_the_post_thumbnail_url( get_the_ID(), 'medium' ) : 'https://placehold.jp/150x150.png'; ?> <article class="news-item"> <a href="<?php the_permalink(); ?>" class="news-item__inner"> <div class="news-item__media js-img-bg"> <img src="<?php print $thumbnail; ?>" alt=""> </div> <div class="news-item__body"> <h2 class="news-item__title"><?php the_title(); ?></h2> <time datetime="the_time( 'Y-m-d' )"><?php the_time( 'Y.m.d' ); ?></time> <p class="news-item__text"><?php print mb_strimwidth( strip_tags( get_the_content() ), 0, 200, "…", "UTF-8" ); ?></p> </div> </a> </article> <?php endwhile; ?> // 追加した部分 <?php set_query_var( 'paging_query', $wp_query ); get_template_part( 'paging' ); ?> // 追加した部分終わり <?php else : ?> 何も投稿がありません。 <?php endif; ?> </main> </div> <?php get_footer(); ?> |
ページネーションはテンプレートパーツのpaging.phpに以下のように記述します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | <?php global $wp_rewrite; $paginate_base = get_pagenum_link( 1 ); if ( strpos( $paginate_base, '?' ) || !$wp_rewrite->using_permalinks() ) { $paginate_format = ''; $paginate_base = add_query_arg( 'paged', '%#%' ); } else { $paginate_format = ( substr( $paginate_base, -1, 1 ) == '/' ? '' : '/' ) . user_trailingslashit( 'page/%#%/', 'paged' ); $paginate_base .= '%_%'; } $page_links = paginate_links( array( 'base' => $paginate_base, //ページネーションのベースのURLを指定。初期値: '%_%' 'format' => $paginate_format, // ページネーションの構造の指定。初期値: '?page=%#%' 'total' => $paging_query->max_num_pages, // 全体のページ数。初期値: 1 'end_size' => 1, // ページ番号の両端にいくつの数字を表示するか。初期値: 1 'mid_size' => 2, // 現在のページの両端にいくつ数字を表示するか。(現在のページは含まない)初期値: 2 'current' => ( $paged ? $paged : 1 ), // 現在のページ番号。初期値: 0 'prev_text' => '前へ', // 前のページへのリンクに表示するテキスト。初期値: '« Previous' 'next_text' => '次へ', // 次のページへのリンクに表示するテキスト。初期値: 'Next »' ) ); if ( strcmp( $page_links, "" ) != 0 ) : ?> <div class="paging-area"> <?php print $page_links; ?> </div><!-- /paging-area --> <?php endif; ?> |
するとページネーションが記事一覧の下に表示されていると思います。
クリックすると遷移します。
2ページ目以降が表示されず404ページになる場合は、管理画面の設定 > パーマリンク設定で何も変更せずに変更を保存ボタンを押してください。
すると表示されたりします。
あとはサブループで以下のように取得して表示させた場合、
1 2 3 4 5 6 7 8 9 10 11 | <?php $news_args = array( 'post_type' => array('news'), 'post_status' => 'publish', 'posts_per_page' => 6, 'paged' => $paged, ); $news_query = new WP_Query($news_args); ?> |
posts_per_pageで1ページに何件表示するか指定しています。
その件数が
・functions.phpで指定した最大投稿数の数
・(functions.phpで指定していない場合)管理画面の設定 > 表示設定で1ページに表示する最大投稿数
よりも小さい場合、後の方のページが表示されないことがあります。
サブループで取得して、2ページ以降が表示されないという場合は見直してみてください。
コードの解説
テンプレートパーツを読み込んでページネーションを表示しました。
テンプレートパーツはテンプレートの変数にアクセスできないので、読み込む前にset_query_var()で渡したい値をメインクエリの指定したクエリ変数の値にセットしています。要は別のテンプレートでも使えるようにグローバルな値にしているのですね。
今回は$wp_queryの値を持つ「paging_query」というグローバル変数をセットしています。
別のテンプレートに値を渡す方法がこれしかなかったのですが、WordPress5.5からテンプレートに引数を渡すことが可能になったみたいです。
Passing arguments to template files in WordPress 5.5
1 2 3 4 5 6 7 8 9 10 11 | get_template_part( $slug ); get_template_part( $slug, $name ); // $slug : 一般テンプレートのスラッグ名 // $name : 特定テンプレートの名前 // get_template_part( 'nav' )→nav.phpを呼び出す // get_template_part( 'nav', 'single' )→nav-single.phpを呼び出す |
1 2 3 4 5 6 | set_query_var( $var, $value ); // $var : クエリ変数を指定するキー // $value : クエリ変数にセットする値 |
paging.phpで実行しているpaginate_links()でページネーションを取得して、表示してます。
1 2 3 4 5 | paginate_links( $args ); // $args : 配列 |
$argsにページネーションの設定を色々指定しています。
詳しくはこちらをどうぞ。
ページネーションは以下のようなHTML構造で出力されます。
1 2 3 4 5 6 7 8 9 10 | <div class="paging-area"> <a class="prev page-numbers" href="生成されたリンク">前へ</a> <a class="page-numbers" href="生成されたリンク">1</a> <a class="page-numbers" href="生成されたリンク">2</a> <span class="page-numbers current">3</span> <a class="page-numbers" href="生成されたリンク">4</a> <a class="next page-numbers" href="生成されたリンク">次へ</a> </div><!-- /paging-area --> |
静的コーディングの段階から、出力されるタグにスタイルが当たるようにしておきたいです。
また、ページネーションを取得する前に下記の記述がありました。
1 2 3 4 5 6 7 8 9 10 11 | global $wp_rewrite; $paginate_base = get_pagenum_link( 1 ); if ( strpos( $paginate_base, '?' ) || !$wp_rewrite->using_permalinks() ) { $paginate_format = ''; $paginate_base = add_query_arg( 'paged', '%#%' ); } else { $paginate_format = ( substr( $paginate_base, -1, 1 ) == '/' ? '' : '/' ) . user_trailingslashit( 'page/%#%/', 'paged' ); $paginate_base .= '%_%'; } |
これはざっくり説明すると、ページネーションのURLの形を
・http://example.jp/news/?paged=2
・http://example.jp/news/page/2/
のどちらの形にするかページネーションのURLから判定して、その形に沿うように出力するようにしています。
ターム一覧を表示
投稿にカテゴリーをつけることができますよね。
右側の空間に、投稿に紐付いているタクソノミー「tax_news」のターム一覧を表示させようと思います。
これもテンプレートパーツに書くので、archive-news.phpで呼び出していきます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 | <?php get_header(); ?> <div class="page-news"> <main class="news-archive"> <?php if ( have_posts() ) : ?> <?php while ( have_posts() ) : the_post(); $thumbnail = ( has_post_thumbnail() ) ? get_the_post_thumbnail_url( get_the_ID(), 'medium' ) : 'https://placehold.jp/150x150.png'; ?> <article class="news-item"> <a href="<?php the_permalink(); ?>" class="news-item__inner"> <div class="news-item__media js-img-bg"> <img src="<?php print $thumbnail; ?>" alt=""> </div> <div class="news-item__body"> <h2 class="news-item__title"><?php the_title(); ?></h2> <time datetime="the_time( 'Y-m-d' )"><?php the_time( 'Y.m.d' ); ?></time> <p class="news-item__text"><?php print mb_strimwidth( strip_tags( get_the_content() ), 0, 200, "…", "UTF-8" ); ?></p> </div> </a> </article> <?php endwhile; ?> <?php set_query_var( 'paging_query', $wp_query ); get_template_part( 'paging' ); ?> <?php else : ?> 何も投稿がありません。 <?php endif; ?> </main> // 追加した部分 <?php get_sidebar( 'news' ); ?> // 追加した部分終わり </div> <?php get_footer(); ?> |
今回は独自のテンプレートパーツを作成せずに、sidebar.phpから派生したsidebar-news.phpを作って以下のように記述しました。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | <?php $terms = get_terms( 'tax_news' ); ?> <aside class="news-sidebar"> <?php if ( !empty( $terms ) ) : ?> <h2 class="ttl">カテゴリー</h2> <ul class="category-list"> <?php foreach ( $terms as $term ) : ?> <li> <a href="<?php print get_term_link( $term ); ?>"> <?php print $term->name; ?> </a> </li> <?php endforeach; ?> </ul> <?php endif; ?> </aside> |
投稿にタームを指定すると、上の画像のように表示されると思います。
コードの解説
get_header()、get_footer()と同じような感じでget_sidebar()でテーマディレクトリ内のsidebar.phpを読み込むことができます。
引数を指定することで、sidebar-引数名.phpを読み込もうとします。
でsidebar-news.php内でget_terms()でタクソノミーのリストを取得しています。
1 2 3 4 5 6 | $terms = get_terms( $taxonomies, $args ); // $taxonomies : タームを取得するタクソノミー // $args : 戻り値の種類を変更 |
今回はタクソノミー「tax_news」のターム一覧を取得したかったので、引数に指定します。
戻り値として、タームのオブジェクトの配列を返すのでforeachで回してから、表示させています。
詳しくは以下をどうぞ。
ターム一覧ページの実装
ターム別の記事一覧の表示ですね。ほぼ記事一覧ページと同じなので、さらっと紹介します。
使うテンプレートはtaxonomy-カスタムタクソノミー名.phpです。
今回だとtaxonomy-tax_news.phpになります。taxonomy-news.phpではないので、注意してください。
トップページにカスタム投稿タイプを〇件出力
トップページに最新のお知らせを3件だけ表示など、よくあるかなと思います。
これは先ほど軽く触れたサブループの出番です!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | <?php $args = array( 'post_type' => array('news'), 'post_status' => 'publish', 'posts_per_page' => 3, ); $the_query = new WP_Query($args); ?> <?php if($the_query->have_posts()): ?> <?php while ($the_query->have_posts()) : $the_query->the_post(); ?> // 内容の表示 <?php endwhile; ?> <?php endif; ?> <?php wp_reset_query(); // リセット ?> |
コードの解説
サブクエリを取得する方法としては
①WP_Queryクラスを使う
②get_posts()を使う
が挙げられます。
メインクエリで出てきた$wp_queryはWP_Queryのインスタンスなので、
似たように使用できるWP_Queryをよく使います。get_posts()で取得できるデータは$wp_query->postsに限定されるので、注意ですね。
記事データを取得するだけなら、どちらを使っても問題ないと思います。
WP_Queryを使用するときは、下のサイトを見ながらやっています。ブクマ推奨ですね。
WP_Queryの使い方をPHPコードにまとめた便利なコード・スニペット
サブループで忘れてはいけないのは、wp_reset_query()ですね。
最後に必ず記述しましょう。この関数によって、グローバルな投稿データをメインクエリのものに戻すことができます。
まとめ
今まで慣習みたいなものとしてコードを書いていました・・・
解説しようとしたら用語の意味や処理を1行ずつ追っていく必要があり、勉強になりました。
最初はとりあえず動かしてみて、コードを追っていく方が理解しやすくていいのではないかと思います。
WordPressを構築するときに忘れがちになるのですが、管理画面をクライアントが使いやすくするようにすることも大切です。
そんなときは、こちらですね。
【WordPress】クライアント目線で使いやすく!管理画面カスタマイズのポイント8つと対応方法まとめ
また、管理画面の投稿一覧でメタ情報やターム情報をみたいという要望があります。
そんなときにはこちらの記事が参考になります。
【WordPress】管理画面で、タクソノミーやカスタムフィールドの列を追加する(ソート機能付き)
参考になった部分があれば幸いです。