2026年5月20日

2026年5月20日

WordPressプラグインでAJAX処理を実装する方法

はじめに

WordPressのAJAX処理は wp_ajax_{action} フックと wp-admin/admin-ajax.php を通じて実装します。wp_localize_script() でJavaScriptにnonce・URLを渡し、セキュアなAJAX通信を実現する方法を解説します。

症状・原因

  • ページリロードなしでデータを保存・取得したい
  • フォームの非同期送信を実装したい
  • ログイン済みと非ログインユーザー両方でAJAXを使いたい
  • WordPressのAJAXとREST APIの使い分けを知りたい

解決手順

ステップ1:JavaScriptファイルを登録してnonceを渡す

// プラグインファイル
add_action('wp_enqueue_scripts', function(): void {
    wp_enqueue_script(
        'myplugin-ajax',
        plugin_dir_url(__FILE__) . 'js/ajax.js',
        ['jquery'],
        '1.0.0',
        true // フッターに出力
    );

    // JavaScriptにPHPの値を渡す
    wp_localize_script('myplugin-ajax', 'mypluginAjax', [
        'ajaxUrl' => admin_url('admin-ajax.php'),
        'nonce'   => wp_create_nonce('myplugin_ajax_nonce'),
        'i18n'    => [
            'sending'  => '送信中...',
            'success'  => '保存しました',
            'error'    => 'エラーが発生しました',
        ],
    ]);
});

ステップ2:PHPでAJAXハンドラーを登録する

// ログイン済みユーザー向け
add_action('wp_ajax_myplugin_save_note', 'myplugin_ajax_save_note');

// 非ログインユーザーも許可する場合
add_action('wp_ajax_nopriv_myplugin_save_note', 'myplugin_ajax_save_note');

function myplugin_ajax_save_note(): void {
    // nonce 検証(セキュリティチェック)
    check_ajax_referer('myplugin_ajax_nonce', 'nonce');

    // 権限チェック(任意)
    if (!current_user_can('edit_posts')) {
        wp_send_json_error(['message' => '権限がありません'], 403);
    }

    // 入力値を取得・サニタイズ
    $title   = sanitize_text_field($_POST['title'] ?? '');
    $content = wp_kses_post($_POST['content'] ?? '');

    if (empty($title)) {
        wp_send_json_error(['message' => 'タイトルは必須です'], 400);
    }

    // データを保存
    $post_id = wp_insert_post([
        'post_title'   => $title,
        'post_content' => $content,
        'post_status'  => 'publish',
        'post_type'    => 'post',
    ]);

    if (is_wp_error($post_id)) {
        wp_send_json_error(['message' => $post_id->get_error_message()], 500);
    }

    // 成功レスポンスを返す(wp_die()は不要)
    wp_send_json_success([
        'post_id'  => $post_id,
        'message'  => '保存しました',
        'edit_url' => get_edit_post_link($post_id),
    ]);
}

ステップ3:JavaScriptでAJAXリクエストを送る

// js/ajax.js
jQuery(function ($) {
    $('#save-note-form').on('submit', function (e) {
        e.preventDefault();

        const $btn = $(this).find('[type="submit"]');
        $btn.prop('disabled', true).text(mypluginAjax.i18n.sending);

        $.ajax({
            url:    mypluginAjax.ajaxUrl,
            method: 'POST',
            data: {
                action:  'myplugin_save_note',         // wp_ajax_{action}
                nonce:   mypluginAjax.nonce,
                title:   $('#note-title').val(),
                content: $('#note-content').val(),
            },
            success(response) {
                if (response.success) {
                    alert(response.data.message);
                    console.log('投稿ID:', response.data.post_id);
                } else {
                    alert(response.data.message || mypluginAjax.i18n.error);
                }
            },
            error() {
                alert(mypluginAjax.i18n.error);
            },
            complete() {
                $btn.prop('disabled', false).text('保存');
            },
        });
    });
});

ステップ4:Fetch APIを使ったモダンな実装

// jQuery不要のモダンな実装
document.getElementById('save-note-form')?.addEventListener('submit', async (e) => {
    e.preventDefault();

    const form = e.target;
    const data = new FormData();
    data.append('action',  'myplugin_save_note');
    data.append('nonce',   mypluginAjax.nonce);
    data.append('title',   form.querySelector('#note-title').value);
    data.append('content', form.querySelector('#note-content').value);

    try {
        const res  = await fetch(mypluginAjax.ajaxUrl, { method: 'POST', body: data });
        const json = await res.json();

        if (json.success) {
            console.log('保存成功:', json.data);
        } else {
            console.error('エラー:', json.data?.message);
        }
    } catch (err) {
        console.error('通信エラー:', err);
    }
});

ステップ5:WP-CLIでAJAXをデバッグする

# admin-ajax.php のエンドポイントを確認
wp eval "echo admin_url('admin-ajax.php');"

# nonceを生成してテスト
wp eval "echo wp_create_nonce('myplugin_ajax_nonce');"

# curlでAJAXリクエストをシミュレート(管理者Cookieが必要)
curl -X POST https://example.com/wp-admin/admin-ajax.php \
  -d 'action=myplugin_save_note&nonce=NONCE&title=テスト&content=本文'

# AJAXアクションの登録を確認
wp eval "global \$wp_filter; print_r(array_keys(\$wp_filter['wp_ajax_myplugin_save_note']->callbacks ?? []));"

ステップ6:REST APIとの使い分け

WordPress AJAX(admin-ajax.php):
→ jQueryが使える環境 / 既存のWordPressフックと連携
→ 非ログインユーザーには wp_ajax_nopriv_ が必要
→ セッション・Cookie認証が必要なケースに適している

REST API(/wp-json/):
→ モダンなJavaScript・外部アプリとの連携
→ Application Passwordで認証
→ GET/POST/PUT/DELETEをHTTPメソッドで使い分け
→ 外部からのアクセスやStateless APIに適している

注意事項

  • check_ajax_referer() は失敗時に自動的に wp_die() します。エラーハンドリングは不要です
  • wp_send_json_success() / wp_send_json_error() は内部で wp_die() を呼び出すため、その後のコードは実行されません
  • wp_ajax_nopriv_ は認証不要のため、レートリミットや入力バリデーションを厳重にしてください

まとめ

WordPressのAJAXは wp_ajax_{action} フックにハンドラーを登録し、wp_localize_script() でnonce・URLをJavaScriptに渡します。check_ajax_referer() でCSRFを防ぎ、wp_send_json_success() / wp_send_json_error() でJSON形式でレスポンスを返します。

お気軽にご相談ください

お見積りへ お問い合わせへ