初心者さんが陥りがちなWordPressループのミス:固定ページにブログの新着を取得表示+ページ内容も表示(※情報古いです)

初心者さんが陥りがちなWordPressループのミス:固定ページにブログの新着を取得表示+ページ内容も表示(※情報古いです)

2013年2月4日
クリックで拡大

#2013.03.25追記
この記事を公開してから2年以上が経ちました。このページに記載している実装方法内には、現在おすすめできないものも含まれています。
今後は「今さら…いや今だから「WordPressのトップページに、新着記事○件を表示する」サンプルコードをあげとく」を参照してください。

#2011.12.12追記 記載内容について、加筆訂正しました。

2年ほど面倒を見ていた、元会社の後輩君からWordPressに関する相談が。
どうやら、サイトのトップページにお知らせとブログの新着記事タイトル5件を出したいけど上手くいかないとのこと。

仕上がりはこんな感じ。(右図)
ブログの新着記事+自由記述でお知らせを掲載したいとのこと。

1.現状把握(後輩君がやっていたこと)

クリックで拡大
  • 固定ページ「HOME」を作り、そこへページテンプレート(toppage.php)を適用。
  • 「ブログ」は、通常の投稿機能を使用。投稿表示のために、「ブログ」という空の固定ページを作成。
  • そして、ダッシュボードの「設定>表示設定>フロントページの表示」で「HOME」を、「投稿ページ」で「ブログ」をそれぞれ選択。

ここまでが設定してありました。

2.症状(何が上手くいっていなかったのか)

クリックで拡大

肝心の症状を書いてませんでした。
こうなっちゃうんです。

「HOME」っていう固定ページの内容を表示させたいのに、上で取得したブログ新着記事1件目が表示されてしまっている状態です。

3.ソースを見てみよう!

問題の部分だけ抜き出してみます。

<h2>新着ブログ一覧</h2>
<dl class="news">
<?php $posts = get_posts('numberposts=5&order=desc');
foreach($posts as $post): ?>
<dt><?php echo date("Y年m月d日", strtotime($post->post_date)); ?></dt>
<dd><a href="<?php the_permalink(); ?>"><?php the_title(); ?></a></dd>
<?php endforeach; ?>
</dl>
 
<h2>お知らせ</h2>
<!-- 投稿ここから -->
<?php if(have_posts()): while(have_posts()): the_post(); ?>
<div class="post">
<?php the_content(); ?>
</div><!-- /.post -->
<?php endwhile; endif; ?>
<!-- /投稿ここまで -->

ちなみに、「ブログの新着をTOPに表示させる方法はどれを参考にした?」って訊いたら、
「コレ→『[WordPress]ワードプレスでトップページなどに新着情報を表示する方法』だそうですので貼っておきます。

4.解決法その1 順番を変える

私も同様の経験があるのですぐピンときました。
説明抜きで、最も手っ取り早く解決するなら、通常のループを先に持ってくることですかね。

<h2>お知らせ</h2>
<!-- 投稿ここから -->
<?php if(have_posts()): while(have_posts()): the_post(); ?>
<div class="post">
<?php the_content(); ?>
</div><!-- /.post -->
<?php endwhile; endif; ?>
<!-- /投稿ここまで -->
 
<h2>新着ブログ一覧</h2>
<dl class="news">
<?php $posts = get_posts('numberposts=5&order=desc');
foreach($posts as $post): ?>
<dt><?php the_time('Y年n月j日'); ?></dt>
<dd><a href="<?php the_permalink(); ?>"><?php the_title(); ?></a></dd>
<?php endforeach; ?>
</dl>

これだと、ただ順番入れ替えるだけで解決です。
(※日付を出力するタグも、WP標準のthe_timeに変更しています。)
つまりは、上に記述したget_posts() をそのまま引き継いじゃってるんですね。

5.解決法その2 query_posts() 関数を使う ※2013.07.21追記:現在はおすすめしません

いやいや、レイアウト上入れ替えは無理だ、って気持ち分かります。
て、ことで、ちゃんとCodexにも記載があります。

テンプレートタグ/query posts – WordPress Codex 日本語版

query_posts は WordPress ループ で表示される投稿を変更するために使います。

上掲ページの「使い方」のスニペットを参考に、ループ部分を書き換えて

<?php if(have_posts()): while(have_posts()): the_post(); ?>

<?php query_posts(); //コレを追記
if(have_posts()): while(have_posts()): the_post(); ?>

てな感じにしてやればOKです。
あとは念のため、ループ閉じる時にwp_reset_query();を付けるとなおよし。

ついでにCodexページには、新着5件を取得時に使っているget_posts()関数についても説明があります。あわせて参照を!

6.解決法その3 get_posts内で、$postsと$postという変数名を使わない

最後の、この方法が最も良いかと思います。
いつもお世話になってる「まがりんさん」こと@jim0912さんがフォーラムで回答してました。

get_postsを利用されるのであれば、$postsと$postという変数名を使わなければ問題ありません。

WordPress › フォーラム » get_postsでループ処理をした後のthe_contentの内容について

7.解決法その4. setup_postdata($post); を適切に使う 【11/12/12追記】

今まで書いてきた話って、グローバル変数とかそのへんの話をしなくてはいけないので、初心者さんにはちょっとハードルが高いのですよね。
なので今回は割愛するんですが、基本ループの「the_post()」にあたると思われるsetup_postdata($post); の記述を加えることで、投稿データがグローバル変数に設定されるという…
このへんは、もう少しちゃんと噛み砕いて1本記事書きたいところです。今回は不親切に結果だけ記載。
この方法が、Codexにも書いてある書き方なので変数って何?とか、そういうの分からないひとはこれ使っとけばいいと思います。多分。

<h2>新着ブログ一覧</h2>
<dl class="news">
<?php
$news = get_posts('numberposts=5&order=desc');
foreach($news as $post):
setup_postdata($post);
?>
<dt><?php the_time('Y年n月j日(D) H:i'); ?></dt>
<dd><a href="<?php the_permalink(); ?>"><?php the_title(); ?></a></dd>
<?php endforeach; ?>
</dl>

以上です。
覚えちゃえば簡単なんですけど、初めてだと焦りますよね。

※引数のnumberposts=5は、query_postsタグに倣ってposts_per_page=5でもOK。