CommentOut

【WordPress】WP_Queryを使っても先頭固定記事が取得できない 【WordPress】WP_Queryを使っても先頭固定記事が取得できない

【WordPress】WP_Queryを使っても先頭固定記事が取得できない

公開日:  最終更新日:

オリジナルテーマを作っていて、先頭固定記事が先頭に固定されていないことに気づきました。

調べても「get_postsを使っていたのが原因だった。WP_Queryを使えば解決した」という情報は出てくるのですが、WP_Queryを使っても先頭に固定されない!

WP_Queryを使っても先頭固定記事が取得できない現象

WP_Queryの全てで先頭固定記事が先頭固定されないわけではなく、特定条件下で先頭固定が効かなくなります。

<?php
// 先頭固定が『有効』なパターン
$result = new WP_Query(array(
    'post_status' => 'publish',
    'post_type' => 'post',
    'posts_per_page' => 10,
    'orderby' => 'date',
    'order' => 'DESC'
));
if ($result->have_posts()):  while ($result->have_posts()): $result->the_post();
    // 記事表示
endwhile; endif;
// サブループをリセット
wp_reset_postdata();
?>

上記の場合は先頭固定が有効です。

<?php
// 先頭固定が『効かない』パターン
$result = new WP_Query(array(
    'post_status' => 'publish',
    'post_type' => 'post',
    'cat' => get_category_by_slug("news")->term_id,
    'posts_per_page' => 10,
    'orderby' => 'date',
    'order' => 'DESC'
));
if ($result->have_posts()):  while ($result->have_posts()): $result->the_post();
    // 記事表示
endwhile; endif;
// サブループをリセット
wp_reset_postdata();
?>

上記例では先頭固定機能が効きません。
違いはカテゴリーで絞り込みを行っているか否かです。

こちらも他のサイトで「絞り込んだカテゴリーに先頭固定記事が含まれていないんじゃないか?」という書き込みがありましたが、私の環境で発生した時には該当記事がカテゴリーに含まれていました。

これは致命的なバグなのでは・・・

WP_Queryでカテゴリー絞り込みを行っても先頭固定を行う方法

現状、WP_Queryで先頭固定を効かせる方法は、先に先頭固定記事だけ取得して、表示後に残りの記事を表示する方法です。

<?php
$slug = "news"; // ←ここだけ都度変えてね!
// 除外する投稿IDの配列
// ※2ループ目で同じ記事が表示されないように、先頭固定記事として取得した記事IDを保存しておき、除外条件に指定する
$exclusion_posts = array();

// 先に先頭固定表示記事のみ取得
$result = new WP_Query(array(
    'post_status' => 'publish',
    'post_type' => 'post',
    'post__in' => get_option('sticky_posts'), // 先頭固定記事のみ取得する条件
    'posts_per_page' => get_option('posts_per_page'), // WordPress設定の投稿表示数
    'cat' => get_category_by_slug($slug)->term_id, // newsカテゴリーで絞り込み
    'orderby' => 'date',
    'order' => 'DESC',
));
// サブループ
if ($result->have_posts()): while ($result->have_posts()): $result->the_post();
    // 除外する記事IDとして配列に追加
    $exclusion_posts[] = get_the_ID();
    // --- 記事表示を行う ---
endwhile; endif;
wp_reset_postdata(); // 投稿データをリセット

// 残りの記事を取得
$result = new WP_Query(array(
    'post_status' => 'publish',
    'post_type' => 'post',
    'post__not_in' => $exclusion_posts, // さっき表示した投稿IDを除外する
    'posts_per_page' => get_option('posts_per_page') - $result->post_count, // 取得記事数から先ほど表示した記事件数を減らす
    'cat' => get_category_by_slug($slug)->term_id, // newsカテゴリーで絞り込み
    'orderby' => 'date',
    'order' => 'DESC',
));
// サブループ
if ($result->have_posts()): while ($result->have_posts()): $result->the_post();
    // --- 記事表示を行う ---
endwhile; endif;
wp_reset_postdata(); // 投稿データをリセット ?>

これで先頭固定記事を先に表示させ、足りない件数分残りの記事を表示してくれます。

例えば、WordPressで表示記事数が10件と設定されており、先頭固定記事が3件あった場合、3件先頭固定記事を表示した後、残りの7件を追加表示してくれます。
さらに、$exclusion_postsがあることで、先頭固定記事として表示した記事が2ループ目に、また表示されることを防いでいます。

現状ではこれが最善の対策なのかなーと思います。

投稿の表示は“テンプレートパーツ化”しましょう

上記の例では投稿の表示部分が”先頭固定記事を表示する時”と”先頭固定じゃない記事を表示する時”の2回ありますよね。
これ制作時は問題が出ないことが多いのですが、アップデートを繰り返していくと「先頭固定の方は更新したけど、もう一方を更新忘れてた」などで、前半と後半で表示デザインが崩れるなどのミスが発生しやすくなります。
なので、このような同じ処理が繰り返される所は”テンプレートパーツ化”しましょう!

テンプレートパーツは以下のコードで呼び出すことが出来ます。

get_template_part($slug, $name, array(key => value, …));

実際に投稿を表示するテンプレートパーツを作ってみましょう。

記事の表示部分を以下のように記述していると仮定します。

<a class="post_block" href="<?php echo get_the_permalink(); ?>">
    <img class="post_block__thumbnail" src="<?php echo get_the_post_thumbnail_url(get_the_ID(), 'full') ?>" alt="<?php echo get_the_title(); ?>" />
    <span class="post_block__title"><?php echo get_the_title(); ?></span>
    <span class="post_block__date"><?php echo get_the_time('Y年m月d日'); ?></span>
    <span class="post_block__author"><?php echo get_the_author(); ?></span>
    <span class="post_block__content"><?php echo str_replace('\n', '', mb_substr(strip_tags(get_the_content()), 0, 240, 'UTF-8')).'...'; ?></span>
</a>

テーマフォルダの中にtemplatesフォルダを作成し、この部分をまるまるコピーして、『post_block.php』などの名前でtemplatesフォルダ内に保存しましょう。

記事の表示部を別名で保存したら、先ほどの投稿呼び出し部分にget_template_partを使います。

<?php
$slug = "news"; // ←ここだけ都度変えてね!
// 除外する投稿IDの配列
// ※2ループ目で同じ記事が表示されないように、先頭固定記事として取得した記事IDを保存しておき、除外条件に指定する
$exclusion_posts = array();

// 先に先頭固定表示記事のみ取得
$result = new WP_Query(array(
    'post_status' => 'publish',
    'post_type' => 'post',
    'post__in' => get_option('sticky_posts'), // 先頭固定記事のみ取得する条件
    'posts_per_page' => get_option('posts_per_page'), // WordPress設定の投稿表示数
    'cat' => get_category_by_slug($slug)->term_id, // newsカテゴリーで絞り込み
    'orderby' => 'date',
    'order' => 'DESC',
));
// サブループ
if ($result->have_posts()): while ($result->have_posts()): $result->the_post();
    // 除外する記事IDとして配列に追加
    $exclusion_posts[] = get_the_ID();
    // ここで記事表示テンプレートを呼び出し
    get_template_part("templates/post_block");
endwhile; endif;
wp_reset_postdata(); // 投稿データをリセット

// 残りの記事を取得
$result = new WP_Query(array(
    'post_status' => 'publish',
    'post_type' => 'post',
    'post__not_in' => $exclusion_posts, // さっき表示した投稿IDを除外する
    'posts_per_page' => get_option('posts_per_page') - $result->post_count, // 取得記事数から先ほど表示した記事件数を減らす
    'cat' => get_category_by_slug($slug)->term_id, // newsカテゴリーで絞り込み
    'orderby' => 'date',
    'order' => 'DESC',
));
// サブループ
if ($result->have_posts()): while ($result->have_posts()): $result->the_post();
    // ここで記事表示テンプレートを呼び出し
    get_template_part("templates/post_block");
endwhile; endif;
wp_reset_postdata(); // 投稿データをリセット ?>

これで『記事をどのように表示するか』については、テンプレートで共有化されたので、テンプレートを書き換えるだけで、両方の表示が変わります。
また、別パターンを作りたい時には、『post_block-grid.php』など、ハイフン○○のような名前を付けてやります。
そして、テンプレートの呼び出し時に以下のように呼び出すと別パターンにも対応可能です。

<?php get_template_part("templates/post_block", "grid"); ?>

さらに、get_template_partで呼び出したテンプレートパーツには変数を渡してやることができるので、何か渡す値によって異なる挙動をさせることも可能です。

宣伝
WordPressサイトのテンプレート編集やトラブル対応、バグ修正、簡単なJavascriptの作成(カルーセルやバリデーション等)など、小規模なスポット対応を受け付けております。
もしお困りごとがありましたら、お問い合わせフォームよりご相談ください。

この記事を書いた人

uilou

uilou

プログラマー

基本的に、自分自身の備忘録のつもりでブログを書いています。 自分と同じ所で詰まった人の助けになれば良いかなと思います。 システムのリファクタリングを得意としており、バックエンド、フロントエンド、アプリケーション、SQLなど幅広い知識と経験があります。 広いだけでなく、知識をもっと深堀りしていきたいですね。