ACFは案件の定番です。WordPressの案件では、Advanced Custom Fields(ACF)を使う機会がとても多いです。今回は、実際の案件でどんな使い方をしているか、特に気をつけているポイントを中心にまとめます。
フィールドグループは管理画面UIで作る
基本方針はUI一択です。ACFには、PHPコード(acf_add_local_field_group())でフィールドグループを定義する方法もありますが、基本的には管理画面のUIから作成するようにしています。理由はシンプルで、コードで登録したフィールドグループは、ACFの管理画面の「フィールドグループ」一覧に表示されず、あとから見返したときに設定内容が分かりにくくなるからです。自分ひとりで完結する案件ならまだしも、あとから別の人が引き継ぐ可能性を考えると、管理画面から見て構成が分かる状態にしておく方が、運用面でのメリットが大きいと感じています。
取得した値の型は必ず正規化する
ACFで一番つまずきやすいのが、フィールドの設定によってget_field()の戻り値の型が変わることです。例えば画像フィールドは「配列」「ID」「URL」のどれで返すか設定で選べますし、投稿オブジェクトを選択するリレーションフィールドも「単一」か「複数」かで戻り値の形が変わります。管理画面側の設定を後から変更すると、テンプレート側のコードが急に動かなくなることもあります。
これを避けるため、表示側のテンプレートでは値を受け取った直後に、必ず配列化・ID抽出などの防御的な正規化をしてから使うようにしています。例えばリレーションフィールドなら、こんな形です。
$related = get_field('related_posts');
// 「単一」設定でも「複数」設定でも同じ配列として扱えるようにする
$related = $related ? (array) $related : array();
foreach ( $related as $item ) {
// 「投稿オブジェクト」設定ならWP_Postが、「投稿ID」設定なら数値が返るので両対応しておく
$post_id = is_object($item) ? $item->ID : (int) $item;
echo get_the_title($post_id);
}
画像フィールドも同様に、戻り値の形式(配列/ID/URL)を最初に決め打ちせず、こう受けています。
$image = get_field('thumbnail');
$image_url = is_array($image) ? ($image['sizes']['medium'] ?? $image['url']) : wp_get_attachment_image_url($image, 'medium');
多少コード量は増えますが、フィールド設定の変更に振り回されにくくなり、結果的に保守性が上がります。
任意項目はまとめてガードする
やり方は決まっています。ACFで作った複数の任意フィールドを出力する際は、個々のフィールドをその場その場でif文チェックする書き方は避けています。まず値をすべて変数に代入してから、ブロック全体を1つの条件分岐でガードする、という形を徹底しています。
$tel = get_field('tel');
$fax = get_field('fax');
$mail = get_field('mail');
if ( $tel || $fax || $mail ) {
echo '<dl class="c-info">';
if ( $tel ) { echo '<dt>TEL</dt><dd>' . esc_html($tel) . '</dd>'; }
if ( $fax ) { echo '<dt>FAX</dt><dd>' . esc_html($fax) . '</dd>'; }
if ( $mail ) { echo '<dt>MAIL</dt><dd>' . esc_html($mail) . '</dd>'; }
echo '</dl>';
}
効果は明確です。こうしておくと、すべての項目が空の場合に空の<dl>タグだけが出力されてしまう、というありがちなミスを防げます。HTMLとPHPを交互に書かず、構造はPHPでまとめて組み立てて文字列として一括出力する形にすれば、条件分岐の見通しも良くなります。
再利用ブロックはショートコードで組み立てる
固定ページの本文に埋め込む再利用ブロック(お知らせ一覧、施工事例カードなど)は、inc/配下にPHPファイルを置き、独自ショートコードとして呼び出す構成にしています。ショートコード側はshortcode_atts()でファイル名などのパラメータを受け取り、対象のPHPファイルをinclude()するだけの薄いラッパー関数にとどめています。本文側では[block name="news"]のように呼び出すだけで済むので、投稿者側の負担も減らせますし、デザイン側の実装をブロックエディタの制約から切り離せるのもメリットです。
繰り返しフィールドとレイアウトの使い分け
工夫はここにあります。同じデータを複数のレイアウト(グリッド表示・スライダー表示など)で出したい場面もよくあります。この場合、データそのものやACFのフィールド構成を複製するのではなく、共通のPHPファイルにレイアウトを切り替える引数を渡す形で対応しています。ショートコードの属性でlayout="slider"のように渡し、1つのファイルの中で分岐させる、という書き方です。データとロジックを複製しないことで、後からデータ構造を変更する際の修正範囲を最小限にできます。
アイキャッチ・サムネイルのフォールバック
地味だけど効く処理です。ACFとは直接関係ありませんが、案件で必ずと言っていいほど必要になるのが、サムネイル画像のフォールバック処理です。アイキャッチ画像が設定されていればそれを使い、なければ本文内の最初の画像を拾い、それもなければテーマ内のデフォルト画像を表示する、という優先順位をヘルパー関数として1つ用意し、一覧・カード系のテンプレートで共通利用するようにしています。この処理を各テンプレートに個別に書いてしまうと、あとから優先順位を変えたくなったときに修正箇所が散らばってしまうので、最初から共通化しておくことを徹底しています。
まとめ
奥は深いです。ACFは自由度が高い分、テンプレート側の書き方次第で保守性が大きく変わるツールだと感じています。特に「型の防御的な正規化」と「任意項目のブロック単位のガード」は、案件を重ねるごとに徹底するようになったポイントです。同じようにACFを使っている方の参考になれば嬉しいです。
ACFに初めて触れる方向けに、そもそも何のためのプラグインなのかを整理した入門記事も書きました。

