ACFのリピーターフィールドで複数データを扱うときの落とし穴|現場で使える実践テクニック

C
クリオ
Web制作ディレクター / フロントエンジニア

こんにちは!

今日は「ACFのリピーターフィールドで複数データを扱うときの落とし穴」について解説します。

リピーターフィールドってそもそも何?

ACFのリピーターフィールドって、僕も最初めっちゃ便利だと思ったんですよ。
複数のデータセットを好きなだけ追加できて、編集画面も直感的で。
でも現場で使ってると、あることに気づくんです。

「あれ、これで大丈夫?」ってなることが、ほんま多い。

リピーターフィールドは、簡単に言うと「同じ構造のデータを何個でも繰り返して登録できるフィールド」です。
例えば、商品紹介ページで「商品の仕様情報」を複数件登録したいときとか、イベント情報で「タイムテーブル」を何個も追加したいときに使う。
めっちゃ便利なんですけど、この便利さゆえに設計を甘く見ると、後々コードが複雑になったり、クライアント更新が大変になったりするんです。

現場で見かける3つの設計ミス

ミス1: データ数の上限を決めずに設計

僕も昔やってしまったんですけど、クライアントに「何個でも追加できますよ!」って言うのは便利なようで、実は危険なんです。
なぜかというと、編集画面でスクロールしまくるようになるし、フロントエンドでも無限にデータをループ処理するコードになってしまう。

現場あるあるなんですけど、こういう場合ありませんか?

  • 「こんなに大量に登録するお客さんいないだろう」と思ってた→実は100件登録されてた
  • 編集画面が重くなって、クライアントから「更新が遅い」と文句がくる
  • フロントエンドで「最新5件だけ表示するつもりが、全件読み込まれてる」ことに後から気づく

ほんま、これらは避けたい。
だから最初の設計段階で、「最大何件までにする?」と必ずクライアントに確認しておくといいですよ。

ミス2: ネストされたリピーターの過度な使用

ACFには「リピーター内にリピーターを入れる」ってことができるんです。
これがめっちゃ便利に見えるんですけど、管理画面が複雑になるし、データベースから取得するときのコードも複雑になる。

例えば、「セクション」の中に「項目」があって、その「項目」の中に「詳細情報」がある…みたいな3階層構造にしちゃうと、後でアクセスするとき$field['section'][0]['item'][0]['detail']みたいなことになって、ほんま読みづらい。

僕の経験上、ネストは最大1階層(リピーター内に別のフィールドがある程度)に抑えておく方が無難です。

ミス3: リピーター内に「テキスト○個」を詰め込む

これ、本当によく見るミスなんです。
「商品情報」という1つのリピーターに、商品名、説明、価格、在庫、カテゴリー、タグ…みたいに、関係ないデータまで詰め込んじゃうパターン。

データベース設計の観点からいうと、これは「正規化されていない」状態です。
クライアント更新を考えたときに、「商品カテゴリーだけ一括変更したい」みたいな要件が出たときに、修正が難しくなるんです。
データベースの観点だけじゃなく、UXの観点でも編集画面がゴチャゴチャになって、間違い入力が増えたりします。

実践的な正しい使い方

設計段階でのチェックリスト

リピーターフィールドを使う前に、こんなことを確認するといいですよ。

  1. データ数の上限は決まってるか?
    「最大20件まで」とか「通常3〜5件」とか、事前にクライアントと詰めておく。
  2. 本当にリピーターが必要か?
    もしかして、別の投稿タイプの方がいいのでは?と考え直す。
  3. データの関連性は適切か?
    入れようとしてるデータが、本当に同じグループでいいのか確認する。
  4. 取得側のコードは複雑にならないか?
    テンプレートでhave_rows()のループが3階層以上になってないか想像してみる。

実装例:シンプルな「タイムテーブル」リピーター

例えば、イベントページのタイムテーブルを作る場合、こんな感じでいいですよ。

ACF設定(管理画面)
フィールドグループ: Event Details
フィールド: schedule (リピーターフィールド)
リピーター内のサブフィールド:
time (テキスト) → 「10:00」
title (テキスト) → 「オープニング」
description (テキストエリア) → 説明文
speaker (関連投稿) → スピーカーを別の投稿から選択

このときのポイントは、speakerのような「別の投稿」を参照するデータは、リピーター内に直接テキスト入力させるのではなく、post_objectrelationshipで関連付けることです。
そうするとクライアント側でも管理しやすいし、後でスピーカー情報を一括変更したいってなったときも対応しやすい。

テンプレートでの取得
テンプレートファイル(例: template-parts/schedule.php)では、こんな感じで取得します。

if ( have_rows( 'schedule' ) ) :
  while ( have_rows( 'schedule' ) ) : the_row();
    $time = get_sub_field( 'time' );
    $title = get_sub_field( 'title' );
    $description = get_sub_field( 'description' );
    $speaker_id = get_sub_field( 'speaker' );
  endwhile;
endif;

ほんまシンプルで、後で修正するときも分かりやすいですよ。

クライアント更新を考慮した設計

「クライアント更新」を考えたリピーター設計は、本当に大事です。
クライアントが「明日から新しい情報に更新したい」ってなったときに、スムーズに対応できるかどうかで、その後の評判も変わってきます。

例えば、商品情報リピーターなら:

  • 商品画像は「画像フィールド」で、各リピーター行で個別に設定できるようにする