T-CREATOR

ACF かブロックか:WordPress 入力 UI の設計判断と移行戦略

ACF かブロックか:WordPress 入力 UI の設計判断と移行戦略

WordPress サイトの構築において、管理画面の入力 UI をどう設計するかは、開発者にとって重要な判断ポイントです。従来から使われている ACF(Advanced Custom Fields)と、WordPress 5.0 以降の標準となったブロックエディタ。どちらを選ぶべきか、あるいは両方を組み合わせるべきか。本記事では、それぞれの特性を理解し、プロジェクトに最適な選択をするための判断基準と、実践的な移行戦略をご紹介します。

背景

WordPress の入力 UI の進化

WordPress の入力インターフェースは、時代とともに大きく変化してきました。

クラシックエディタの時代、カスタムフィールドの入力には主に ACF(Advanced Custom Fields)が使われていました。ACF は直感的な管理画面で、テキストフィールド、画像アップロード、リピーターフィールドなど、多彩な入力フィールドを簡単に追加できるプラグインです。多くの開発者に愛され、WordPress サイト構築の定番ツールとして確固たる地位を築いてきましたね。

2018 年、WordPress 5.0 のリリースとともに、ブロックエディタ(Gutenberg)が標準となりました。これは記事の編集方法を根本から変える革新的なアプローチでした。コンテンツを「ブロック」という単位で管理し、ドラッグ&ドロップで自由に配置できる新しい体験を提供したのです。

ブロックエディタの登場がもたらした変化

ブロックエディタの登場は、単なる編集画面の刷新ではありませんでした。WordPress のコンテンツ管理の思想そのものを変えるものだったのです。

以下の図は、ACF とブロックエディタの基本的なデータフローの違いを示しています。

mermaidflowchart TB
    subgraph ACF["ACF によるデータ管理"]
        acf_admin["管理画面<br/>カスタムフィールド"] -->|メタデータとして保存| acf_meta[("post_meta<br/>テーブル")]
        acf_meta -->|get_field で取得| acf_theme["テーマテンプレート<br/>PHP で出力"]
    end

    subgraph Block["ブロックによるデータ管理"]
        block_admin["ブロックエディタ<br/>ブロック単位で編集"] -->|HTMLコメント付き<br/>コンテンツとして保存| block_content[("post_content<br/>カラム")]
        block_content -->|パース&レンダリング| block_output["フロントエンド<br/>ブロックテーマまたは<br/>クラシックテーマ"]
    end

ACF では、データは post_meta テーブルに保存され、テーマ側で PHP の get_field() 関数を使って取得します。一方、ブロックエディタでは、ブロック構造が HTML コメントとともに post_content カラムに保存され、レンダリング時にパースされるという仕組みです。

現在の開発現場における選択肢

現在の WordPress 開発現場では、3 つの選択肢が存在します。

ACF を継続利用する方法では、従来の開発フローをそのまま維持できますね。既存のプロジェクトが多数ある場合、チームメンバーが ACF に慣れている場合には、この選択が合理的でしょう。

ブロックエディタへ完全移行する方法は、WordPress の標準機能を最大限活用するアプローチです。将来性を重視し、フルサイト編集(FSE)を見据えた開発を行いたい場合に適しています。

ACF とブロックを併用する方法は、それぞれの長所を活かすハイブリッドアプローチです。実は、ACF 6.0 以降では ACF ブロック機能が強化され、この併用が非常にスムーズになっているんです。

課題

ACF の抱える課題

ACF は非常に優れたツールですが、いくつかの課題も抱えています。

WordPress の進化への対応という点では、ACF はサードパーティプラグインであるため、WordPress コアの進化に追従する必要があります。フルサイト編集(FSE)やブロックテーマが標準となる中で、ACF の位置づけをどうするか、開発者は判断を迫られていますね。

学習コストと依存性の問題もあります。ACF に依存した開発を続けると、チームメンバー全員が ACF の使い方を習得する必要があります。また、プラグインへの依存度が高まり、将来的な選択肢が狭まる可能性も考慮すべきでしょう。

パフォーマンスへの影響も無視できません。多数のカスタムフィールドを持つページでは、get_field() の呼び出しが増え、データベースクエリが増加します。適切なキャッシング戦略を実装しないと、サイトの表示速度に影響が出る場合があります。

ブロックエディタの抱える課題

一方、ブロックエディタにも課題があります。

開発の複雑性が最大の障壁でしょう。カスタムブロックの開発には、React、JavaScript、JSX、webpack などのモダンな JavaScript 技術スタックの知識が必要です。従来の PHP 中心の WordPress 開発とは大きく異なるため、学習曲線が急峻になります。

以下の図は、カスタムブロック開発に必要な技術スタックの関係性を示しています。

mermaidflowchart LR
    dev["開発者"] -->|学習必須| react["React"]
    dev -->|学習必須| jsx["JSX"]
    dev -->|理解必須| wp_api["WordPress<br/>Block API"]

    react --> component["ブロック<br/>コンポーネント"]
    jsx --> component
    wp_api --> component

    component -->|ビルド| webpack["webpack/<br/>@wordpress/scripts"]
    webpack -->|出力| js_bundle["バンドル JS"]

    js_bundle -->|登録| wp_register["register_block_type()"]
    wp_register -->|利用可能| editor["ブロック<br/>エディタ"]

既存テーマとの互換性も課題です。クラシックテーマからブロックテーマへの移行は、テンプレート構造の全面的な見直しを伴います。段階的な移行が難しく、一度に大きな変更を強いられる可能性があります。

編集者の学習コストも考慮が必要です。ACF のシンプルなフォームに慣れた編集者にとって、ブロックエディタの自由度の高さが逆に混乱を招くケースもあります。特に、決まったフォーマットでコンテンツを入力してほしい場合、ブロックの自由度が足かせになることもあるのです。

判断を難しくする要因

これらの課題に加えて、判断を難しくする要因がいくつかあります。

プロジェクトの性質の多様性です。シンプルなコーポレートサイト、複雑な EC サイト、メディアサイト、会員制サイトなど、要件は千差万別。どの UI が最適かは、プロジェクトごとに異なりますね。

既存資産の存在も大きな要因です。すでに ACF で構築された大量のページがある場合、移行コストは膨大になります。移行によるメリットがコストを上回るかどうか、慎重な判断が求められます。

チームのスキルセットも重要です。PHP が得意なチームと、React が得意なチームでは、最適な選択が異なるでしょう。

解決策

判断基準の確立

ACF かブロックかを判断するには、明確な基準が必要です。以下の観点から評価しましょう。

プロジェクト特性による判断

コンテンツの構造化レベルを確認します。

#構造化レベル最適な選択理由
1高度に構造化(決まった入力項目)ACFフォーム形式の入力が適している
2柔軟な構成(ページごとに異なるレイアウト)ブロック自由度の高い編集が可能
3両方の要素を含むACF ブロック(併用)構造化部分は ACF、可変部分はブロック

編集者のスキルレベルも重要な判断材料です。

#編集者のスキル最適な選択理由
1WordPress 初心者ACFシンプルなフォーム入力
2WordPress 経験者ブロック柔軟な編集が活かせる
3混在ACF ブロック用途に応じて使い分け

技術的要件による判断

将来性と拡張性の観点では、以下のように整理できます。

  • フルサイト編集(FSE)を活用したい → ブロックエディタが必須
  • 長期的なメンテナンス性を重視 → WordPress 標準のブロックが有利
  • 短期的な開発速度を重視 → ACF が効率的
  • 既存資産の活用が必要 → ACF の継続利用または段階的移行

開発チームのスキルセットも考慮します。

#チームの強み推奨アプローチ補足
1PHP 開発が得意ACF 中心学習コストを抑えられる
2React / JS が得意カスタムブロック開発技術力を最大限活用
3両方のスキルあり用途に応じて選択最適なツールを使い分け

ハイブリッドアプローチの実践

最も現実的な解決策は、ACF とブロックを併用するハイブリッドアプローチです。

ACF ブロックの活用

ACF 6.0 以降では、ACF で定義したフィールドをブロックとして登録できる「ACF ブロック」機能が大幅に強化されました。これにより、ACF の使いやすさとブロックエディタの柔軟性を両立できるのです。

以下の図は、ACF ブロックの仕組みを示しています。

mermaidflowchart TB
    acf_def["ACF フィールド定義<br/>(PHP または JSON)"] -->|ブロックとして登録| register["acf_register_block_type()"]
    register -->|ブロック化| block["ACF ブロック"]

    block -->|エディタで配置| editor["ブロックエディタ"]
    editor -->|ブロック設定を<br/>フォームで入力| acf_form["ACF 入力フォーム"]

    acf_form -->|データ保存| block_attrs["ブロック属性<br/>(post_content内)"]
    block_attrs -->|レンダリング| template["ブロックテンプレート<br/>(PHP)"]
    template -->|出力| frontend["フロントエンド表示"]

ACF ブロックでは、フィールド定義は従来通り ACF で行いつつ、ブロックエディタ内で使えるブロックとして登録できます。編集者は ACF のシンプルなフォームで入力し、ブロックとして自由に配置できるという、両方の良いところ取りが可能になりますね。

使い分けの戦略

実践的な使い分けの戦略は以下の通りです。

メタ情報には ACF を使う

投稿やページ全体に関わるメタ情報(SEO 設定、表示設定、関連情報など)は、従来通り ACF のカスタムフィールドとして実装します。これらは構造化されたデータであり、フォーム形式の入力が適しています。

コンテンツ本体にはブロックを使う

記事本文や、ページごとに異なるレイアウトが必要な部分には、ブロックエディタを活用します。標準ブロックと、必要に応じて ACF ブロックやカスタムブロックを組み合わせましょう。

繰り返し利用する構造化コンテンツには ACF ブロックを使う

スタッフ紹介、サービス紹介、FAQ など、決まった形式で繰り返し使うコンテンツは、ACF ブロックとして実装すると効率的です。

移行戦略の設計

既存の ACF サイトからブロックへ移行する場合、段階的なアプローチが重要です。

段階的移行のロードマップ

フェーズ 1:環境整備では、以下を実施します。

  1. 現状の ACF フィールド構成を棚卸し
  2. WordPress とプラグインを最新版にアップデート
  3. ACF を 6.0 以降にアップグレード
  4. 開発環境でブロックエディタの動作確認

フェーズ 2:ハイブリッド化に進みます。

  1. 新規コンテンツはブロックエディタで作成
  2. 既存の ACF フィールドは維持
  3. 一部の ACF フィールドを ACF ブロックに変換
  4. 編集者向けのトレーニング実施

フェーズ 3:本格移行で完了します。

  1. 重要度の低いページから順次移行
  2. データ移行スクリプトの作成と実行
  3. 移行後の動作検証
  4. 不要になった ACF フィールドの段階的削除

リスク軽減策

移行にはリスクが伴います。以下の対策を講じましょう。

バックアップの徹底は最重要です。移行前に完全なバックアップを取得し、すぐに復元できる体制を整えます。データベースとファイルの両方をバックアップしましょう。

段階的なロールアウトにより、影響範囲を限定します。一度に全てを移行せず、一部のコンテンツタイプや一部のページから始めることで、問題が発生しても影響を最小限に抑えられます。

ロールバック計画の策定も必須です。移行がうまくいかなかった場合に、どのように元の状態に戻すか、事前に手順を明確にしておきましょう。

具体例

ACF ブロックの実装例

実際に ACF ブロックを実装する手順を見ていきましょう。

ステップ 1:ACF フィールドグループの作成

まず、ACF の管理画面でフィールドグループを作成します。今回は「スタッフ紹介ブロック」を例にします。

管理画面での設定は以下のようになります。

  • フィールドグループ名:スタッフ紹介ブロック
  • フィールド
    • 名前(テキスト)
    • 役職(テキスト)
    • プロフィール写真(画像)
    • 紹介文(テキストエリア)

ステップ 2:ブロックの登録

functions.php でブロックを登録します。

php<?php
/**
 * ACF ブロックの登録
 * スタッフ紹介ブロックをブロックエディタで使えるようにする
 */
add_action('acf/init', 'register_staff_block');

function register_staff_block() {
    // ACF が有効化されているか確認
    if (function_exists('acf_register_block_type')) {
        // スタッフ紹介ブロックを登録
        acf_register_block_type(array(
            'name'              => 'staff',
            'title'             => __('スタッフ紹介'),
            'description'       => __('スタッフのプロフィールを表示するブロック'),
            'render_template'   => 'template-parts/blocks/staff/staff.php',
            'category'          => 'formatting',
            'icon'              => 'admin-users',
            'keywords'          => array('staff', 'スタッフ', 'プロフィール'),
        ));
    }
}

この関数では、acf_register_block_type() を使ってブロックを登録しています。render_template でレンダリング用のテンプレートファイルを指定していますね。

ステップ 3:ブロックテンプレートの作成

次に、ブロックの表示用テンプレートを作成します。

php<?php
/**
 * スタッフ紹介ブロックのテンプレート
 * ACF フィールドの値を取得して表示する
 *
 * @param array $block ブロックの設定情報
 */

// ACF フィールドから値を取得
$name = get_field('name');
$position = get_field('position');
$photo = get_field('photo');
$bio = get_field('bio');

// ブロックに適用する CSS クラスを準備
$class_name = 'staff-block';
if (!empty($block['className'])) {
    $class_name .= ' ' . $block['className'];
}
if (!empty($block['align'])) {
    $class_name .= ' align' . $block['align'];
}
?>

このテンプレートの冒頭部分では、ACF フィールドから値を取得し、CSS クラスを準備しています。

php<div class="<?php echo esc_attr($class_name); ?>">
    <?php if ($photo): ?>
        <div class="staff-block__photo">
            <img src="<?php echo esc_url($photo['url']); ?>"
                 alt="<?php echo esc_attr($photo['alt']); ?>">
        </div>
    <?php endif; ?>

    <div class="staff-block__content">
        <?php if ($name): ?>
            <h3 class="staff-block__name">
                <?php echo esc_html($name); ?>
            </h3>
        <?php endif; ?>

        <?php if ($position): ?>
            <p class="staff-block__position">
                <?php echo esc_html($position); ?>
            </p>
        <?php endif; ?>

        <?php if ($bio): ?>
            <div class="staff-block__bio">
                <?php echo wp_kautoembed($bio); ?>
            </div>
        <?php endif; ?>
    </div>
</div>

HTML の出力部分では、各フィールドの値を適切にエスケープして表示しています。画像、名前、役職、紹介文がそれぞれ表示されますね。

ステップ 4:スタイルの追加

ブロックに CSS を適用します。

css/**
 * スタッフ紹介ブロックのスタイル
 */
.staff-block {
  display: flex;
  gap: 2rem;
  padding: 2rem;
  background-color: #f5f5f5;
  border-radius: 8px;
}

.staff-block__photo {
  flex-shrink: 0;
  width: 150px;
  height: 150px;
  overflow: hidden;
  border-radius: 50%;
}

基本的なレイアウトスタイルです。フレックスボックスを使って横並びにし、写真を円形に切り抜いています。

css.staff-block__photo img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.staff-block__content {
  flex: 1;
}

.staff-block__name {
  margin: 0 0 0.5rem;
  font-size: 1.5rem;
  font-weight: bold;
  color: #333;
}

画像のフィット方法と、コンテンツ部分のスタイルを定義しています。

css.staff-block__position {
  margin: 0 0 1rem;
  font-size: 1rem;
  color: #666;
}

.staff-block__bio {
  font-size: 0.95rem;
  line-height: 1.6;
  color: #444;
}

/* レスポンシブ対応 */
@media (max-width: 768px) {
  .staff-block {
    flex-direction: column;
    text-align: center;
  }

  .staff-block__photo {
    margin: 0 auto;
  }
}

モバイル表示では縦並びに切り替え、中央揃えにしています。

カスタムブロックの実装例(React)

より高度な要件には、React を使ったカスタムブロックが適しています。

ステップ 1:開発環境のセットアップ

@wordpress​/​scripts を使って開発環境を整えます。

bash# プラグインディレクトリに移動
cd wp-content/plugins/my-custom-blocks

# package.json の作成
yarn init -y

# WordPress スクリプトをインストール
yarn add @wordpress/scripts --dev

これで WordPress の推奨するビルド環境が整います。

ステップ 2:package.json の設定

ビルドスクリプトを追加します。

json{
  "name": "my-custom-blocks",
  "version": "1.0.0",
  "scripts": {
    "build": "wp-scripts build",
    "start": "wp-scripts start",
    "format": "wp-scripts format",
    "lint": "wp-scripts lint-js"
  },
  "devDependencies": {
    "@wordpress/scripts": "^26.0.0"
  }
}

start コマンドで開発モード、build コマンドで本番用ビルドができます。

ステップ 3:ブロックの JavaScript 実装

src​/​index.js にブロックのコードを記述します。

javascript/**
 * WordPress のブロック関連モジュールをインポート
 */
import { registerBlockType } from '@wordpress/blocks';
import { useBlockProps, RichText, MediaUpload } from '@wordpress/block-editor';
import { Button } from '@wordpress/components';
import { __ } from '@wordpress/i18n';

/**
 * カスタムブロック「FAQ アイテム」を登録
 */
registerBlockType('my-custom-blocks/faq-item', {
    title: __('FAQ アイテム', 'my-custom-blocks'),
    icon: 'editor-help',
    category: 'common',
    attributes: {
        question: {
            type: 'string',
            source: 'html',
            selector: '.faq-item__question',
        },
        answer: {
            type: 'string',
            source: 'html',
            selector: '.faq-item__answer',
        },
    },

ブロックの基本設定です。タイトル、アイコン、カテゴリー、そして保存する属性(question と answer)を定義しています。

javascript    /**
     * エディタ内での編集画面
     * RichText コンポーネントで質問と回答を編集可能にする
     */
    edit: ({ attributes, setAttributes }) => {
        const blockProps = useBlockProps();
        const { question, answer } = attributes;

        return (
            <div {...blockProps}>
                <div className="faq-item">
                    <RichText
                        tagName="h4"
                        className="faq-item__question"
                        value={question}
                        onChange={(value) => setAttributes({ question: value })}
                        placeholder={__('質問を入力...', 'my-custom-blocks')}
                    />
                    <RichText
                        tagName="div"
                        className="faq-item__answer"
                        value={answer}
                        onChange={(value) => setAttributes({ answer: value })}
                        placeholder={__('回答を入力...', 'my-custom-blocks')}
                    />
                </div>
            </div>
        );
    },

edit 関数でエディタ内の表示を定義しています。RichText コンポーネントを使うことで、太字や斜体などの装飾も可能になりますね。

javascript    /**
     * フロントエンドでの保存形式
     * 入力された内容を HTML として保存
     */
    save: ({ attributes }) => {
        const blockProps = useBlockProps.save();
        const { question, answer } = attributes;

        return (
            <div {...blockProps}>
                <div className="faq-item">
                    <RichText.Content
                        tagName="h4"
                        className="faq-item__question"
                        value={question}
                    />
                    <RichText.Content
                        tagName="div"
                        className="faq-item__answer"
                        value={answer}
                    />
                </div>
            </div>
        );
    },
});

save 関数でフロントエンドに保存される HTML を定義しています。

ステップ 4:ブロックの登録(PHP 側)

最後に、PHP でブロックを登録します。

php<?php
/**
 * Plugin Name: My Custom Blocks
 * Description: カスタムブロック集
 * Version: 1.0.0
 */

/**
 * ブロックアセットの登録
 */
function my_custom_blocks_register() {
    // ビルドされた JS と CSS を登録
    register_block_type(__DIR__ . '/build');
}
add_action('init', 'my_custom_blocks_register');

register_block_type() にビルドディレクトリを渡すだけで、自動的にアセットが読み込まれます。

移行スクリプトの例

既存の ACF データをブロックに移行するスクリプトの例です。

データ移行の基本的な流れ

php<?php
/**
 * ACF フィールドからブロックへのデータ移行スクリプト
 * WP-CLI コマンドとして実行
 */

if (!defined('WP_CLI') || !WP_CLI) {
    return;
}

/**
 * スタッフ紹介の ACF データをブロックに移行するコマンド
 */
WP_CLI::add_command('migrate-staff-to-blocks', function($args, $assoc_args) {
    // 移行対象の投稿を取得
    $posts = get_posts(array(
        'post_type' => 'page',
        'posts_per_page' => -1,
        'meta_query' => array(
            array(
                'key' => 'staff_members', // ACF リピーターフィールド
                'compare' => 'EXISTS',
            ),
        ),
    ));

    WP_CLI::log(sprintf('移行対象: %d 件の投稿', count($posts)));

WP-CLI を使って移行スクリプトを実装します。対象となる投稿を取得するクエリを実行していますね。

php    $migrated = 0;
    $failed = 0;

    foreach ($posts as $post) {
        try {
            // 既存の ACF データを取得
            $staff_members = get_field('staff_members', $post->ID);

            if (empty($staff_members)) {
                continue;
            }

            // ブロック形式のコンテンツを生成
            $blocks_content = '';

            foreach ($staff_members as $staff) {
                // ACF ブロックの形式で保存
                $block_attrs = array(
                    'name' => $staff['name'],
                    'position' => $staff['position'],
                    'photo' => $staff['photo'],
                    'bio' => $staff['bio'],
                );

                // ブロックのマークアップを生成
                $blocks_content .= serialize_block(array(
                    'blockName' => 'acf/staff',
                    'attrs' => array(
                        'data' => $block_attrs,
                    ),
                ));
            }

各スタッフのデータを ACF ブロック形式に変換しています。serialize_block() 関数でブロックの HTML コメント形式を生成します。

php            // 投稿の本文を更新
            $result = wp_update_post(array(
                'ID' => $post->ID,
                'post_content' => $blocks_content,
            ), true);

            if (is_wp_error($result)) {
                throw new Exception($result->get_error_message());
            }

            $migrated++;
            WP_CLI::log(sprintf('✓ 移行完了: %s (ID: %d)', $post->post_title, $post->ID));

        } catch (Exception $e) {
            $failed++;
            WP_CLI::warning(sprintf('✗ 移行失敗: %s (ID: %d) - %s',
                $post->post_title, $post->ID, $e->getMessage()));
        }
    }

投稿の本文を更新し、結果をログに出力しています。エラーが発生した場合は警告を表示しますね。

php    // 結果のサマリーを表示
    WP_CLI::success(sprintf(
        '移行完了: %d 件成功, %d 件失敗',
        $migrated,
        $failed
    ));
});

最後に、移行結果のサマリーを表示して完了です。

実際の移行では、このスクリプトを実行する前に必ず以下を確認しましょう。

  1. 本番環境の完全なバックアップを取得
  2. ステージング環境でテスト実行
  3. 少数の投稿で動作確認
  4. 問題がなければ本番環境で実行

まとめ

ACF かブロックかという選択は、単なる技術的な判断ではありません。プロジェクトの要件、チームのスキル、編集者の使いやすさ、将来の拡張性など、多角的な視点から検討する必要があります。

判断のポイントを整理しましょう。

構造化されたデータを扱う場合、決まったフォーマットでの入力が必要な場合は、ACF が優れています。フォーム形式の直感的な入力で、編集者の学習コストを抑えられますね。

一方、ページごとに異なるレイアウトが必要な場合、視覚的に編集したい場合、フルサイト編集を活用したい場合は、ブロックエディタが適しています。WordPress の進化に沿った開発ができるでしょう。

実践的なアプローチとしては、ハイブリッド戦略が現実的です。

ACF 6.0 以降の ACF ブロック機能を活用することで、両方の良いところを組み合わせられます。メタ情報は ACF、コンテンツ本体はブロック、繰り返し使う構造化コンテンツは ACF ブロックという使い分けが効果的ですね。

移行を検討する際は、段階的なアプローチが重要です。

一度に全てを移行するのではなく、新規コンテンツから始め、既存コンテンツは優先度の低いものから順次移行していきます。バックアップとロールバック計画を用意し、リスクを最小限に抑えましょう。

WordPress の入力 UI は、これからも進化を続けます。フルサイト編集の普及、ブロックテーマの標準化、そして新しい編集機能の追加が予想されます。その変化に柔軟に対応できるよう、技術的な選択肢を広げておくことが大切です。

あなたのプロジェクトに最適な選択は何でしょうか。この記事が、その判断の一助となれば幸いです。

関連リンク