はりうすブログ (のすけのメモ)

はりうす代表 ”のすけ”のブログです

WordPressループってなんぞや?コンテンツ表示に欠かせないお話

f:id:hollywis:20180830172637j:plain

こんにちは、のすけです。

大学で非常勤講師などをやらせていただいているのですが

その繋がりで大学時代の教授との縁が復活するなど、 世間は狭いなと思う今日この頃です。

本日はWordPressカスタマイズの第8団「WordPressループってなんぞや?」です。

ちなみに、前回はWordPressで利用すつタグを紹介しました。 これだけは覚えよう!WordPressテンプレートタグ! - hollywis's diary (はりうすブログ)

WordPressループとは?

僕自身はRuby on RailsやNode.jsでプログラムを書くことが多いのですが

そんな、Web畑出身の人が何これ?と思うのがWordPressループではないでしょうか。

何かと言いますと、端的に言うと、コンテンツを表示する時にwhile文を書いて、その中で記事情報を取得し表示までする仕組みのようです。

ちょっと良くわからないですよね。 普通にコンテンツを取ってきて表示すればいいじゃ。。。と思ってしまう。なぜわざわざループさせているのか。 しかもwhileの無限ループを使うなんてバグが出そうな怖い技を使って。。。

そんなにwhile文なんて使いませんからね。組み込み系のプログラム(Arduinoとか)じゃあるまいし。と思ってしまいます。

どこでWordPressループは使われているの?

まず、利用例を紹介します。

以前、書いたindex.phpとstyle.cssだけで作る最小のテーマを例にします。 hollywis.hatenablog.com

詳しくは↑を見てください。

この中で、index.phpにメインページの時、投稿ページの時、固定ページの時のコンテンツの出しわけを行っている部分があります。

<?php if(is_home()) : ?><!--メインページの時 -->
  <?php if ( have_posts() ) : while ( have_posts() ) : the_post(); ?>
    <article class="entry">
      <h2 class="entry-head"><a href="<?php the_permalink(); ?>"><?php the_title(); ?></a></h2>
      <div class="entry-body">
          <?php the_content(); ?>
      </div>
    </article>
<?php endwhile; endif; ?>
<?php elseif(is_single()) : ?><!-- 投稿ページの時 -->
  <?php while ( have_posts() ) : the_post(); ?>
    <h1><?php the_title() ?></h1>
    <div>
      <?php the_content(); ?>
    </div>
<?php endwhile; ?>
<?php elseif(is_page()) : ?><!-- 固定ページの時 -->
    <?php while ( have_posts() ) : the_post(); ?>
       <h1><?php the_title() ?></h1>
       <div>
         <?php the_content(); ?>
      </div>
    <?php endwhile; ?>
<?php endif; ?>

その中で、それぞれのif文の中に while ( have_posts() )からendwhile;の記述があるかと思います。

ここがWordPressループ。ループの中ではリンクURLを取得するthe_permalink()や、ページのタイトルthe_title()、本文を取得するthe_content()を 呼び出しています。

特にthe_content()WordPressループの中でしか機能しません。while文の外で実行するとエラーになります。

なぜループが必要なの?

(ここに呼び出しの図をいずれいれたい)

WordPressはページのリクエストを受け付けると、URLからメインページなのか、投稿ぺージなのか固定ページなのかなどを判断します。 そして、記事の情報をデータベースから取得して、該当するテンプレートファイルを読み込みます。

このデータベースからデータを読み込む際に、記事の情報は$wp_queryという変数に格納されます。 しかし、この状態では表示できる状態にはありません。なぜならなんと、$wp_queryは配列みたいな形式らしいのです。(厳密には配列ではない)

そのため、$wp_queryから記事を順に取り出して表示する方式が取られます。

投稿ページや固定ページでは、もちろんそのページ1ページ分の情報しか$wp_queryに格納されていないため、 実際にはWordPressループであるwhile文は1回しか回りません。 しかし、メインページなど記事の一覧を表示する際には、投稿されている記事の数だけ$wp_queryに格納されている訳ですから何度もループが周り、コンテンツが表示されるという事です。

お気づきになったかと思いますが、一覧ページでは実際にはタイトルや記事の最初の数文字程度の配列が取得できればいいとしても、全ての本文を取得しているという事です。

もし、その1記事1記事の本文の内容が多い場合には、膨大なデータをデータベースから取得して、それを表示する訳ですから、一覧ページのレスポンスは遅くなる設計になっているのではと邪推してしまいます。この辺り汎用性を求めたがうえのWordPressのざっくり設計が気になってしまったりしますね。

もしかしたら上手い事やっている可能性はありますが。

ともあれ、この仕組みの事をWordPressループと呼んでいるようです。

WordPressループを使ってwp_queryからデータを取り出す方法

<?php if(is_home()) : ?><!--メインページの時 -->
  <?php if ( have_posts() ) : while ( have_posts() ) : the_post(); ?>
    <article class="entry">
      <h2 class="entry-head"><a href="<?php the_permalink(); ?>"><?php the_title(); ?></a></h2>
      <div class="entry-body">
          <?php the_content(); ?>
      </div>
    </article>
<?php endwhile; endif; ?>

上記例にあるように、have_posts()関数、the_post();を使います。 その上のif文はページの判別なので今は無視してください。

まず、 if ( have_posts() )で表示するコンテンツ内容があるか確認します。 if ( have_posts() )wp_queryの中身を見に行っているんですね。

そして、もしhave_posts() がブール値でtrueであれば、while文に突入します。 このときwhileの発動条件がまたしてもhave_posts()です。そして、have_posts()ループの中で利用できるthe_post()を呼び出しています。

the_post()とは

それではWordPress Cedexを確認してみましょう。

ループを次の投稿へ進めます。 次の投稿を取得して、それを「現在の投稿」としてセットアップし、ループの 'in the loop' プロパティを true にします。

参考:「現在の投稿」はループの中で the_title() や the_content() 等が対象とする投稿です。

by WordPress Cedex

つまり、wp_queryから順番に投稿を取り出しているのがthe_post()という訳ですね。 すると、the_title()the_content()などの内容が取り出している投稿のものに変更されるという事です。 だから、whileループ中で、タイトルthe_title()や本文the_content()を呼び出すだけで、順々に投稿が表示されます。

なるほど納得ですね。

WordPressループはこれでもう怖くありません。

追加WP_QUERYについて

$wp_queryはクエリ形式でWP_QUERYという関数を使ってデータを取り出せます。

そして、取り出す為に独自のクエリが用意されています。 利用できるクエリはこちらを確認ください。

つまり、個別投稿ページでは自動的にこのクエリを利用してWP_QUERYから投稿を1個に絞り、その結果を$wp_queryに格納している。 固定ページではWP_QUERYから固定ページを1個に絞り、その結果を$wp_queryに格納している。メインページでは、例えば最近の5記事などを取得するクエリをWP_QUERYで取得し、その結果を$wp_queryに格納しているという訳ですね。

また、自動的に取ってくるだけでなく、ページ中に任意に以下のように記述するとこで、WP_QUERYから直接記事を取得する方法があります。

// The Query
$the_query = new WP_Query( $args );

// The Loop
if ( $the_query->have_posts() ) {
    echo '<ul>';
    while ( $the_query->have_posts() ) {
        $the_query->the_post();
        echo '<li>' . get_the_title() . '</li>';
    }
    echo '</ul>';
    /* Restore original Post Data */
    wp_reset_postdata();
} else {
    // no posts found
}

こんな感じで$argsにクエリを記述して$the_queryに取得結果を格納する事で、have_posts()の記述を$the_query->have_posts()に変えるだけで、いつも通り自在に記事情報を取得できたりします。

便利ですね。

次回は最小テーマを拡張して、固定ページpage.phpを作っていきたいと思います。

ではまた。