複数カテゴリーを持つ投稿をsingle.phpで一つのカテゴリー毎にページ送りしたい
-
カテゴリー: A,B,C,D
投稿1:カテゴリーA,Cを持つ
投稿2:カテゴリーA,Dを持つ
投稿3:カテゴリーB,Cを持つ
投稿4:カテゴリーB,Dを持つという括りがあるとして、
カテゴリーリンク一覧(header.php内にwp_list_categoriesで実装)>該当カテゴリーアーカイブページ(category.php)>個別投稿ページ(single.php)と表示させたいのですが、
個別投稿ページのページ送りを、カテゴリーアーカイブページに準じた投稿だけで送られるようにしたいのです。
現在は全ての投稿を取得してページ送りしてしまいます。single.phpでまとめて作成したいのですが、どのように親ページ?のカテゴリーに該当する投稿だけを取得できるでしょうか。<article> <?php if ( have_posts() ) : ?> <?php while ( have_posts() ) : the_post(); ?> <section post_class(); > <?php the_content(); ?> <div> <div class="leftcol"> <?php next_post_link('%link', '← prev', true ); ?> </div> <div class="rightcol"> <?php previous_post_link('%link', 'next →', true ); ?> </div> </div> </section> <?php endwhile; ?> <?php endif; ?> </article>post_linkにtrueを追加して試してみているのですが変わらずです。single.phpにまとめて記述しているからではないかなと思うのですが、その場合クリック元の親ページのカテゴリーの取得、ページ送りへの適用の仕方が分かりません。
-
思いついたのは
WP_Queryでcategory__inとdate_queryを指定する方法ですね。
ループ内に以下のPHPを書いてください。// カテゴリIDの配列 $categories = get_the_category(); $cat_ids = array(); foreach ( $categories as $category ) { $cat_ids[] = $category->term_id; } // 現在の投稿の日時 $post_time = get_the_date('U'); // 前の投稿のクエリ $prev_query = new WP_Query(array( 'posts_per_page' => 1, 'category__in' => $cat_ids, 'date_query' => array( 'before' => $post_time, ), 'order' => 'DESC', )); // 前の投稿のURL if ( $prev_query->have_posts() ) { $prev_query->the_post(); $prev_post_link = get_permalink(); } // 次の投稿のクエリ $next_query = new WP_Query(array( 'posts_per_page' => 1, 'category__in' => $cat_ids, 'date_query' => array( 'after' => $post_time, ), 'order' => 'ASC', )); // 次の投稿のURL if ( $next_query->have_posts() ) { $next_query->the_post(); $next_post_link = get_permalink(); } // 投稿データのリセット wp_reset_postdata();「親ページのカテゴリ」というのが、複数のカテゴリの「いずれか」なのか、「すべて」なのかがわからないので、とりあえず「いずれか」のほうでコードを書きましたが、「すべて」にする場合は
category__inをcategory__andに変更してください。「いずれか」の方で正しいです。
PHPの知識が追いつかず申し訳ないのですが、メインループ内にどのように組み込めばいいでしょうか。
間違い箇所を指摘していただけるとありがたいです。現在は表示されません。<div class="leftcol"> <?php $categories = get_the_category(); $cat_ids = array(); foreach ( $categories as $category ) { $cat_ids[] = $category->term_id; } $post_time = get_the_date('U'); $prev_query = new WP_Query(array( 'posts_per_page' => 1, 'category__in' => $cat_ids, 'date_query' => array( 'before' => $post_time, ), 'order' => 'DESC', )); if( $prev_query->have_posts() ) : while($prev_query->have_posts()): $prev_query->the_post(); $prev_post_link = get_permalink(); ?> <a href="<?php $prev_post_link; ?>">← prev</a> <?php endwhile; endif; wp_reset_postdata(); ?> </div>すみません、コードにいくつかミスがあったので訂正します。
// カテゴリIDの配列 $categories = get_the_category(); $cat_ids = array(); foreach ( $categories as $category ) { $cat_ids[] = $category->term_id; } // 現在の投稿の日時 $post_time = $post->post_date; // 前の投稿のクエリ $prev_query = new WP_Query(array( 'posts_per_page' => 1, 'category__in' => $cat_ids, 'date_query' => array( array( 'before' => $post_time, ), ), 'order' => 'DESC', )); // 前の投稿のURL if ( $prev_query->have_posts() ) { $prev_query->the_post(); $prev_post_link = get_permalink(); } // 次の投稿のクエリ $next_query = new WP_Query(array( 'posts_per_page' => 1, 'category__in' => $cat_ids, 'date_query' => array( array( 'after' => $post_time, ), ), 'order' => 'ASC', )); // 次の投稿のURL if ( $next_query->have_posts() ) { $next_query->the_post(); $next_post_link = get_permalink(); } // 投稿データのリセット wp_reset_postdata();変わったのは以下の2点です。
$post_time = $post->post_date;
「投稿日時」のフォーマットを勘違いしていました。
'date_query' => array( array( 'before' => $post_time, ), ),*_queryのご多分に漏れず、一つ一つをネストした配列に入れるのを失念していました。さて、ループへの組み込み方ですが、あなたのコードは
<a>タグ内の変数をechoしていない点を除いて間違っていません。
間違っていたのはこっちでした。丁寧にありがとうございます、ページ送りの機能は無事組み込むことができました。
しかし、まだ正常に動きません。どこに問題があるのかも見当がつきません。上の例を利用して、
カテゴリーリンク一覧:・A ・B ・C ・Dが表示、AorBorCorDをクリック
カテゴリーアーカイブページ:ページAに投稿1,2が表示、1or2をクリック
ページBに投稿3,4が表示、3or4をクリック
ページCに投稿1,3が表示、1or3をクリック
ページDに投稿2,4が表示、2or4をクリック
個別投稿ページ:ページAからのリンクの場合、投稿1or2を表示、投稿1,2をページ送り
ページBからのリンクの場合、投稿3or4を表示、投稿3,4をページ送り
ページCからのリンクの場合、投稿1or3を表示、投稿1,3をページ送り
ページDからのリンクの場合、投稿2or4を表示、投稿2,4をページ送りという構造で実装したいのです。現在は個別投稿ページのページ送りで、関係のないカテゴリーへも不規則にリンクされてしまい、最終的に最新投稿と最古投稿で行ったり来たりになったりしてしまいます。何が原因でしょうか?
「関係のないカテゴリーへも不規則にリンクされてしまい」とは、投稿1のカテゴリAとは無関係のカテゴリCに属する投稿3にリンクされる、という意味でしょうか。
そうであるなら、WordPressの仕様通りであり、俺のコードの意図する通りですのでなんら問題ありません。
そうでなく全くの無関係の投稿にリンクされるとすれば問題ですが。個別投稿ページの「親ページ」とか「元のページ」というのがカテゴリアーカイブページである、という認識に違和感があります。
ページAからリンクしてもページCからリンクしても、投稿1の内容は同じですし、それを判別する仕組みはWordPressにはありません。
「ページAから来た場合」と「ページCから来た場合」で投稿1の内容を変えるには、URLのGETパラメータ(クエリ文字列)やCookieを使って判別するくらいしか思いつきません。
例えば、ページAにある投稿1へのリンクにはURLに?category=Aを付ける、投稿1ではURLのGETパラメータからリンク元を知り、ページ送りの内容に反映する、とか。
後者はif ( isset($_GET['category']) && $_GET['category'] == 'A' )といった感じでわりと簡単に判別できますが、前者はお使いのテーマ次第ですね。
不可能ではないですが、その場合色々と検討する必要があります。- どのページからのリンクにどのようなGETパラメータを付けるか
- GETパラメータがない場合(カテゴリ以外のページから来た場合)、どのように表示するか
- 思わぬGETパラメータが与えられた場合にどう対処するか
それより、厳密に同一カテゴリ内でのページ送りが必要で、かつひとつの投稿が複数の(それぞれが無関係の?)カテゴリに属する、とはどのような形態なのか、よくわかりません。
投稿が単一のカテゴリのみを持つようサイト構成を見直す、というアプローチもあると思います。上記のコードはそういう意味合いなのですね。でしたら僕の認識違いでした。
そして、上のおっしゃる意味はつまり、カテゴリーAとCを持つ投稿からAのリンクかCのリンクか振り分けを設定することはとても複雑な作業ということでしょうか。WordPress制作が初なもので、何が最適解なのかよくわからない状態なのですが、
例えば、ある投稿が「緑色」と「カエル」というカテゴリーグループに属していて、他の投稿は「緑色」と「キュウリ」だったりして、(カテゴリーはクライアントの方でこれから任意に増えていくとした上で、)「緑色」の投稿だけを集めて一枚ずつ観覧したかったり、「カエル」の投稿だけを集めて一枚ずつ観覧したかったりすることはよくあることだと思いますし、複雑な話ではないはずです。ですので、一つの投稿が並列のカテゴリーに複数属するという認識自体が間違っているということでしょうか。
タクソノミーを使ったり、親子関係をつけたり、カスタムフィールドやタグでそれらが簡単に実装できるのならその構成にしたいのですが、一体何が一般なのかわからず、もし最適な実装方法があればご教示願いたいです。よろしくお願いします。なるほど、理解できました。
そのような分類を行う場合にはカスタムタクソノミーというのを使うのが一般的だそうですが、カテゴリでもできないことはありません(カテゴリもタクソノミーの一種です)。
例:カスタムタクソノミー「色」……緑色、白、黒、etc.
カスタムタクソノミー「生き物」……カエル、スズメ、猫、etc.
いずれにせよ、リンク元を判別する必要はありますが。確かに、特定のカテゴリに属する投稿を片っ端から読みたいという場合はありますね。
そういう場合、普通はカテゴリアーカイブのページにいちいち戻って次の投稿を探す、というふうに使うものだと思います。
しかし、ページ送りの要領で次の投稿に遷移できれば、楽ではありますね。
投稿が属するカテゴリの分だけページ送りを設置する、という方法だと比較的簡単に実装できそうですが、全くスマートな方法ではありませんね……カテゴリアーカイブのページのテーマファイルで、投稿へのリンクのURLを出力する部分にフィルターフックを設置するなどして、出力するURLをGETパラメータ付きに書き換えれば、個別投稿ページ側でリンク元を判別することができます。
また、個別投稿ページからページ送りによって前後の投稿に遷移する場合も、GETパラメータを維持するようにすればよいでしょう。
それらの実装についてはやはりお使いのテーマ次第(特にカテゴリアーカイブのページのテーマファイル)、ということになります。
よって、ここでコードをお示しすることはできませんが、情報をいただければ時間のあるときにコードを書いてみたいと思います。追伸:
上記の方法では、カテゴリアーカイブのページから個別投稿ページに来た場合にしか、元のカテゴリを判別することはできません。
特に昨今ではサイトの訪問者は検索エンジンから直接、個別投稿ページに訪れるという場合がほとんどだといいますから、この仕組みを実装してもあまり使われないかもしれません。
GETパラメータ付きのURLを検索エンジンにインデックスさせるのはまずいですもんね。おっしゃること理解しました。確かにあまり使われないかもしれませんが、今後の制作にも多々応用できそうなので、フィルターフックを用いて実装できる方法があるのであれば、教えていただきたいです。
アーカイブページに当たるcategory.phpはこんな感じですが、the_permalink();の部分をいじる形でしょうか。<section <?php post_class(); ?>> <div> <h3 class="category-name"><?php single_cat_title( '', true ); ?></h3> <?php if ( have_posts() ) : while ( have_posts() ) : the_post(); ?> <a href="<?php the_permalink(); ?>"><?php the_content(); ?></a> <?php endwhile; endif; ?> </div> </section>the_permalink()はthe_permalinkというフィルターフックを内部で適用するので、これに関数をフックするだけでいけますね。
functions.php に以下の内容を追加すると、カテゴリアーカイブページのループ内のthe_permalink()でのみ、URLの末尾に?catid=[カテゴリID]のようなGETパラメータを追加します。function add_category_param( $permalink, $post ) { global $wp_query; if ( $wp_query->is_category && $wp_query->in_the_loop ) $permalink .= '?catid='.$wp_query->get_queried_object_id(); return $permalink; } add_filter( 'the_permalink', 'add_category_param' );そして、このGETパラメータを個別投稿ページの「ページ送り」で拾います。
「次の投稿」と「前の投稿」へのリンクURLにも、GETパラメータを受け継ぎます。
以前の回答のコードからいろいろ変更されているので、ご確認ください。
特に、XSS防止のためのesc_url()はお忘れなく。// カテゴリIDの配列 $categories = get_the_category(); $cat_ids = array(); foreach ( $categories as $category ) { $cat_ids[] = (int) $category->term_id; } $get_string = ''; if ( isset($_GET['catid']) && is_numeric($_GET['catid']) ) { // GETパラメータがある場合 $get_string = '?catid='.$_GET['catid']; $cat_id = (int) $_GET['catid']; if ( in_array($cat_id, $cat_ids, true) ) $cat_ids = array($cat_id); } // 現在の投稿の日時 $post_time = $post->post_date; // 前の投稿のクエリ $prev_query = new WP_Query(array( 'posts_per_page' => 1, 'category__in' => $cat_ids, 'date_query' => array( array( 'before' => $post_time, ), ), 'order' => 'DESC', )); // 前の投稿のURL if ( $prev_query->have_posts() ) { $prev_query->the_post(); $prev_post_link = esc_url(get_permalink().$get_string); } // 次の投稿のクエリ $next_query = new WP_Query(array( 'posts_per_page' => 1, 'category__in' => $cat_ids, 'date_query' => array( array( 'after' => $post_time, ), ), 'order' => 'ASC', )); // 次の投稿のURL if ( $next_query->have_posts() ) { $next_query->the_post(); $next_post_link = esc_url(get_permalink().$get_string); } // 投稿データのリセット wp_reset_postdata();GETパラメータの
catidを持ち、その値が数値で、かつ現在の投稿が含まれるカテゴリのIDのいずれかに一致しなければ、無視するようにしています。
無視した場合の挙動は以前の回答と同じです。すみません、また間違えました。
一つ目のコードの1行目を次のように訂正します。function add_category_param( $permalink ) {フックする関数の引数が1個より多いときは
add_filter()に第4引数を与えなければならないのですが、引数は1個しか使っていないので指定しないことにしました。完璧に実現できました!丁寧にお付き合いいただき大変ありがとうございます。
フィルターフックなど僕の現在の知識にでは少しややこしい技術ですが、繰り返して使いこなせるようになろうと思います。すみません、訂正です。
投稿A,B,C(投稿日時順)をページ送りするとき、A→B→CではなくA→Cと、最新→最古投稿(逆も同じ)へと飛んでしまいます。なので$post_timeのあたりにもう一捻り何かいると思うのですが、現状知識が追いつかず、質問する他ありません。もし何かお知りでしたら教えていただきたいです。date_queryのbeforeとafterは、デフォルトではその日時と全く同じ投稿日の投稿を除外するのですが、まさか投稿日時が秒まで同じで、なんてことはないですよね。
投稿Bでは正常でしょうか。
またorderのASCとDESCを入れ替えてみるとどうなりますか?
トピック「複数カテゴリーを持つ投稿をsingle.phpで一つのカテゴリー毎にページ送りしたい」には新たに返信することはできません。