お問い合わせフォーム用のプラグインSnow Monkey Formsですが、基本、お問い合わせ内容はメールで送信されます。
それだけでなく、スプレッドシートにも書き込みたいときに役に立つカスタマイズを紹介します。
Snow Monkey Forms の設定・カスタマイズ方法はこちらにまとめています。
Snow Monkey Formsでお問い合わせ内容をスプレッドシートに書き込むカスタマイズ
やり方は意外と簡単です。
Snow Monkey Formsの作成
普通にプラグインを入れてお問い合わせフォームを作ります。

お問い合わせ項目に合わせてNAME属性に名前をつけておきましょう。

今回は以下に設定しました。
| 項目 | NAME |
|---|---|
| お名前 | your-name |
| Eメール | your-email |
| メッセージ | your-message |
スプレッドシートの作成
スプレッドシートを用意します。
シート名はお問い合わせにしました。
専用シートの1行目を「リクエストID、日時、フォームID、お名前、メールアドレス、メッセージ」の6列にします。既存データへ上書きせず、テスト用の別シートを用意してください。
| リクエストID | 日時 | フォームID | お名前 | メールアドレス | メッセージ |
GASの作成
スプレッドシートの拡張機能からApps Scriptを選択

以下はPOST専用の受信コードです。既存の実データを使わず、まずテスト用シートで確認してください。
いか、詳細です。
まずは下記のコードをコピペします。
シート名はお問い合わせにしています。
function output_(value) {
return ContentService.createTextOutput(JSON.stringify(value)).setMimeType(ContentService.MimeType.JSON);
}
function doGet() { return output_({ok: false}); }
function doPost(e) {
let lock;
try {
if (!e || !e.postData || e.postData.contents.length > 20000) return output_({ok: false});
const data = JSON.parse(e.postData.contents);
const props = PropertiesService.getScriptProperties();
const secret = props.getProperty('WAZA_SMF_SECRET');
if (!secret || !/^[0-9a-f]{64}$/i.test(secret) || data.secret !== secret) return output_({ok: false});
if (!/^[0-9a-f-]{36}$/i.test(data.request_id || '') || data.form_id !== 115) return output_({ok: false});
for (const [key, max] of [['name',200],['email',320],['message',5000]]) {
if (typeof data[key] !== 'string' || data[key].length > max) return output_({ok: false});
}
const sheetId = props.getProperty('WAZA_SMF_SHEET_ID');
if (!sheetId) return output_({ok: false});
lock = LockService.getScriptLock();
if (!lock.tryLock(5000)) return output_({ok: false});
const sheet = SpreadsheetApp.openById(sheetId).getSheetByName('お問い合わせ');
if (!sheet) return output_({ok: false});
const last = sheet.getLastRow();
if (last > 1 && sheet.getRange(2,1,last-1,1).createTextFinder(data.request_id).matchEntireCell(true).findNext()) {
return output_({ok: true, duplicate: true});
}
const text = value => /^[\s]*[=+@-]/.test(value) ? "'" + value : value;
sheet.appendRow([data.request_id,new Date(),data.form_id,text(data.name),text(data.email),text(data.message)]);
SpreadsheetApp.flush();
return output_({ok: true});
} catch (_) {
return output_({ok: false}); // 本文や秘密値をログに出さない
} finally {
if (lock && lock.hasLock()) lock.releaseLock();
}
}
コードがコピペできたのであれば、新しいデプロイを選択。

ウェブアプリの種類を選択
WordPressから非ログインのHTTPリクエストを受ける構成です。公開前に、スクリプトプロパティへ WAZA_SMF_SECRET(安全な乱数で生成した64桁の16進文字列)と WAZA_SMF_SHEET_ID(保存先のスプレッドシートID)を登録します。コードは秘密値が一致しないPOSTと、すべてのGETを拒否します。組織で匿名Webアプリを許可していない場合は、この構成を使わないでください。
最後にデプロイボタンを押します。
デプロイ時の実行者・アクセス範囲・スプレッドシートへの権限を確認してください。シート自体を一般公開する必要はありません。

承認が終わるとウェブアプリのURLが発行されます。
URLはコピーしてメモしておきましょう。

functions.phpの設定
WordPressに戻ります。
バックアップ後、子テーマのfunctions.phpへ以下を追加します。対象フォームIDの115はPHPとGASの両方で変更してください。PHPではskipフィルターの判定、after_sendの判定、送信データのform_idの3か所をそろえます。メールをスキップするフィルターがある場合は、その処理を先に登録してください。スキップが有効な送信はシートへコピーしません。秘密値やGASのURLをフロント側JavaScriptへ出力しません。

define( 'WAZA_SMF_GAS_URL', 'https://script.google.com/macros/s/REPLACE_WITH_DEPLOYMENT_ID/exec' );
define( 'WAZA_SMF_GAS_SECRET', 'REPLACE_WITH_RANDOM_64_HEX_CHARACTERS' );// 同じ送信処理のスキップ結果を保持する(リクエスト内だけ)。
$waza_smf_sheet_state = new SplObjectStorage();
add_filter( 'snow_monkey_forms/administrator_mailer/skip', function ( $skip, $responser, $setting ) use ( $waza_smf_sheet_state ) {
if ( 115 === (int) $setting->get( 'form_id' ) ) {
$waza_smf_sheet_state[ $responser ] = (bool) $skip;
}
return $skip;
}, PHP_INT_MAX, 3 );
add_action( 'snow_monkey_forms/administrator_mailer/after_send', function ( $sent, $responser, $setting, $parser ) use ( $waza_smf_sheet_state ) {
if ( ! $sent || 115 !== (int) $setting->get( 'form_id' ) ) return;
if ( ! $waza_smf_sheet_state->contains( $responser ) ) return;
$skipped = $waza_smf_sheet_state[ $responser ];
$waza_smf_sheet_state->detach( $responser );
if ( $skipped ) return; // スキップの true をメール成功として扱わない。
if ( ! defined( 'WAZA_SMF_GAS_URL' ) || ! defined( 'WAZA_SMF_GAS_SECRET' ) || ! preg_match( '/^[0-9a-f]{64}$/i', WAZA_SMF_GAS_SECRET ) ) return;
if ( ! preg_match( '~^https://script\.google\.com/macros/s/[A-Za-z0-9_-]+/exec$~', WAZA_SMF_GAS_URL ) ) return;
$values = $responser->get_all();
$payload = array( 'secret' => WAZA_SMF_GAS_SECRET, 'request_id' => wp_generate_uuid4(), 'form_id' => 115 );
foreach ( array( 'name' => 'your-name', 'email' => 'your-email', 'message' => 'your-message' ) as $key => $field ) {
$value = $values[ $field ] ?? '';
if ( ! is_string( $value ) ) return;
$payload[ $key ] = $value;
}
$response = wp_remote_post( WAZA_SMF_GAS_URL, array(
'timeout' => 10,
'headers' => array( 'Content-Type' => 'application/json' ),
'body' => wp_json_encode( $payload ),
) );
$ok = ! is_wp_error( $response ) && 200 === wp_remote_retrieve_response_code( $response );
$body = $ok ? json_decode( wp_remote_retrieve_body( $response ), true ) : null;
if ( ! is_array( $body ) || true !== ( $body['ok'] ?? false ) ) {
error_log( 'WAZA SMF sheet copy failed. Request: ' . $payload['request_id'] );
}
}, 10, 4 );
テスト
以上の設定が終わったらテストしましょう。

架空の名前・example.comのアドレスで送信し、メール成功後にシートへ1行入ること、秘密値が違う要求は書き込まれないこと、同じリクエストIDの再送は重複しないことを確認します。先頭が「=」などの値は数式化を避けるため文字列として保存します。
まとめ
まとめると今回のカスタマイズは以下のことをやっています。
- Snow Monkey Forms のお問い合わせフォーム送信を検知
- 入力された内容(名前・メール・メッセージ)とフォームIDを取得
- Google Apps Script(GAS)を通じて、Googleスプレッドシートに自動で1行ずつ書き込み
このカスタマイズをベースにAIなど使って応用してみてください。
検証範囲:ローカルWordPressで送信失敗時・別フォームの除外とPOST本文の構築、GAS相当のスタブで認証・型・長さ・重複・数式対策を確認しました。実GASへのデプロイ、リダイレクトを含む通信、シート保存・実メール送信は未検証です。
この例はメール成功後のコピーで、シート書込失敗をフォームの送信失敗には変えません。自動再送キューはなく、PHPフックが別の呼び出しとして再実行されると新しいIDになります。厳密な一度だけの保存や欠損ゼロを保証しません。失敗時はリクエストIDだけをログに残します。停止する場合はPHPフックを削除し、使わなくなったWebアプリのデプロイを停止します。共有先と保存期間も運用で決めてください。
仕様参照:GoogleのWebアプリ仕様、LockService。



