べんりワザ・WordPressのカスタムフィールド(ACF・SCF)

ACFの繰り返しフィールドの出し方

ACF(SCF)のくり返しフィールドをテンプレートに出す方法。have_rows・the_row・get_sub_fieldの3点セット、入れ子のくり返し、無限ループになる原因までコード付きで解説します。

ACF(SCF)のくり返しフィールド(リピーター)は、have_rows()the_row()get_sub_field()3点セットで出します。投稿ループとそっくりな形なので、パターンを一度覚えれば使い回せます。

<?php if (have_rows('items')) : ?>
  <?php while (have_rows('items')) : the_row(); ?>
    <?php the_sub_field('title'); ?>
  <?php endwhile; ?>
<?php endif; ?>
べんりワザACFとSCF、どっちを入れればいい?ACFとSCFの違いと選び方はこちら1

くり返しフィールドは「行の集まり」

くり返しフィールドは、同じ入力欄のセット(行)を何個でも増やせるフィールドです。たとえば「よくある質問」なら、質問と答えのセットを行として何個でも追加できます。

よくある質問(くり返しフィールド:faq)
├── 1行目  question: 送料はいくら?   answer: 全国一律500円です
├── 2行目  question: 返品できる?     answer: 到着後7日以内なら…
└── 3行目  question: ギフト包装は?   answer: 無料で承ります

外側の名前(faq)がくり返し全体、中の名前(question)がサブフィールド。この親子関係が、そのままコードの形になります。

基本の型:3点セットで書く

<?php if (have_rows('faq')) : ?>
  <dl class="faq">
    <?php while (have_rows('faq')) : the_row(); ?>
      <dt><?php the_sub_field('question'); ?></dt>
      <dd><?php the_sub_field('answer'); ?></dd>
    <?php endwhile; ?>
  </dl>
<?php endif; ?>

3つの役割はこう分かれています。

  • have_rows('faq')——行があるかを調べる(ifwhileの両方で使う)
  • the_row()——次の行へ進める。これが無いと無限ループ
  • the_sub_field('question')——今の行の中の値を出す(値を変数に入れたいときはget_sub_field()

ifで囲むのは、行が0個のときに空の<dl>だけが残るのを防ぐためです。見出しやラッパー要素ごと出さないようにするのが定番の書き方です。

画像やリンクを含む行

サブフィールドの型が画像・リンクでも、取り出し方は同じget_sub_field()です。

<?php if (have_rows('members')) : ?>
  <ul class="members">
    <?php while (have_rows('members')) : the_row(); ?>
      <li>
        <?php echo wp_get_attachment_image(get_sub_field('photo'), 'medium'); ?>
        <p class="name"><?php the_sub_field('name'); ?></p>
        <?php if ($url = get_sub_field('sns')) : ?>
          <a href="<?php echo esc_url($url); ?>">SNS</a>
        <?php endif; ?>
      </li>
    <?php endwhile; ?>
  </ul>
<?php endif; ?>

画像フィールドは返り値の設定(ID・URL・配列)で書き方が変わる点だけ注意します。

べんりワザACFで画像を表示する方法画像フィールドの返り値ごとの書き方はこちら

入れ子のくり返し(くり返しの中のくり返し)

くり返しフィールドの中に、さらにくり返しフィールドを入れられます。内側でも書き方はまったく同じ——名前だけ変えて3点セットを重ねます。

<?php while (have_rows('plans')) : the_row(); ?>
  <section>
    <h3><?php the_sub_field('plan_name'); ?></h3>

    <?php if (have_rows('features')) : ?>   <!-- 内側のくり返し -->
      <ul>
        <?php while (have_rows('features')) : the_row(); ?>
          <li><?php the_sub_field('feature_text'); ?></li>
        <?php endwhile; ?>
      </ul>
    <?php endif; ?>
  </section>
<?php endwhile; ?>

内側のhave_rows('features')は、今の行(the_row()で進んだ行)の中を見るので、親の名前を書き足す必要はありません。

行数を数える・何行目かを知る

<?php $rows = get_field('faq'); ?>
<p>ぜんぶで<?php echo count($rows); ?>件</p>

<?php while (have_rows('faq')) : the_row(); ?>
  <p>Q<?php echo get_row_index(); ?><?php the_sub_field('question'); ?></p>
<?php endwhile; ?>
  • count(get_field('faq'))——行数(0件のときはget_field()falseを返すので、count()の前にifで確認すると安全)
  • get_row_index()——今が何行目か(1から始まる

foreachで書く方法もある

get_field()でくり返し全体を受け取ると、普通のPHPの配列として扱えます。have_rows()の形が読みにくいときはこちらでも同じ結果になります。

<?php $faq = get_field('faq'); ?>
<?php if ($faq) : ?>
  <?php foreach ($faq as $row) : ?>
    <dt><?php echo esc_html($row['question']); ?></dt>
    <dd><?php echo esc_html($row['answer']); ?></dd>
  <?php endforeach; ?>
<?php endif; ?>

配列のキーはサブフィールドの名前です。ただしこの書き方では画像フィールドなどの整形(返り値の変換)がかからない場合があるため、基本はhave_rows()、単純なテキストだけならforeachと使い分けるのが無難です。

ほかの投稿のくり返しを出す

一覧ページなどで「別の投稿のくり返し」を出したいときは、第2引数に投稿IDを渡します。

<?php while (have_rows('faq', 123)) : the_row(); ?>

固定ページのオプション欄をトップページで使う、といった場面で役立ちます。optionを渡すとオプションページの値を読めます。

まとめ

  1. くり返しフィールドはhave_rows() / the_row() / get_sub_field()の3点セット。投稿ループと同じ形
  2. if (have_rows(...))で囲めば、0件のときに空のリストが残らない
  3. 入れ子も同じ書き方を重ねるだけ。行数はcount(get_field())、何行目かはget_row_index()
  4. the_row()忘れ=無限ループget_field()とサブ用関数の取り違え=値が空——この2つが定番のつまずき

行を増やすだけでコンテンツが伸びる作りにしておくと、更新をおまかせできるサイトになります。

よくある質問

繰り返しフィールドの中の値をget_field()で取ろうとしても空になります
くり返しの中はget_sub_field()(表示だけならthe_sub_field())を使います。get_field()は投稿直下のフィールドを見にいくため、行の中の値は取れません。
ページが真っ白になったり、読みこみが終わりません
whileループの中でthe_row()を書き忘れると、行が次に進まないため無限ループになります。while (have_rows('items')) : the_row(); の形をセットで覚えてください。
繰り返しフィールドはACFの無料版でも使えますか?
ACF(WP Engine版)では有料のACF PROが必要です。WordPress公式配布のSCFには最初から無料で入っているので、SCFならそのまま使えます。
行が何個あるか数えたいです
count(get_field('items'))で行数が分かります。ループの中で「今が何行目か」を知りたいときはget_row_index()を使います(1から始まります)。