FormBridgeではなく、WordPress + Contact Form 7で代替実装した話

Advent Calendar

kintone×トヨクモ かんた〜ん Advent Calendar 2025の2日目の記事です。

  Topへ↓

一年が過ぎるのは早いですね。もうAdvent Calendarの季節がきた!という感じです。
今年は私も代表を譲り、技術を追求する側に戻る決断を下しました。一役員に立ち返って初の技術ブログがこちらです。

今回はFormBridgeネタです。

「FormBridgeの新フォームでkViewerルックアップ動的絞り込みが使えず、WordPress + Contact Form 7で代替実装した話」

という訳で、タイトルだけでわかる人にはわかるネタです。

まあ、FormBridgeをディスっているように見えるかもしれませんが、結論はやっぱりノーコードで作れるFormBridgeはよいよね。という話なのでご安心を!

2.FormBridgeとkViewer

  Topへ↑

FormBridgeは言うまでもなく、簡単にkintoneとつながるWebフォームが作れるサービスです。

Webフォームを簡単に作ることができます。そして、フォームに入力したデータをkintoneに追加/更新するための設定もノーコードで可能なのです。だからこそ、FormBridgeは支持を受けているのでしょう。

要するに、kintoneのレコード詳細/追加/編集フォームを作るように、外に設置するフォームが作られる。それこそが真価です。

Q:kintoneのフォームを作るように。では、ルックアップみたいなこともできる?

A:はい、できます。

ルックアップは別のアプリのデータを取得し、自アプリにデータとしてコピーして保存するための部品ですね。

FormBridgeもkViewerルックアップという部品を設置することで、同じことができます。

kViewerとは、kintoneのデータを外部に公開可能にするサービスです。

kViewerの表示形式は数種類ありますが、【外部公開API】を選択すると、FormBridgeからそのビューが利用でき、フォームの部品にkintoneから取得したデータをもとに設定でき、キー項目以外の項目も同時に取得可能となります。

3.FormBridgeの新旧フォームって?

  Topへ↑

FormBridgeですが、2025/2/4に旧バージョンから新バージョンに切り替わりました。
この記事にも書かれているとおり、今後は旧バージョンのフォームを新しく作ることはできません。

この新旧バージョンのフォームの切り替えですが、実はフォームのデザインだけでなく、カスタマイズの際の記述方法も大きく変わりました。
カスタマイズリファレンスのページを開くと、以下のような画面が表示されます。ここで新旧どちらかを選ぶのです。

ここで旧バージョンのフォームのカスタマイズ、つまり「従来のフォーム」を選択すると、旧バージョンのカスタマイズが出てきます(2025/12/1時点)。

このように新旧両フォームではできることできないことが分かれます。
他の違いはこの記事に書かれていますが、カスタマイズについてはトヨクモさんのサポート対象外ですので、ご自身で解決する必要があります。ご注意ください。

4.え?kViewer ルックアップの動的絞り込みができない!

  Topへ↑

今回、お客様からはkViewerルックアップの表示項目をさらに動的に絞りたい、というご要望がありました。

これは、kViewerルックアップで表示される選択肢の量が多いためです。もちろん、事前に絞り込み設定を行えば、あらかじめ選択肢は絞ることができます。が、今回、それでも選択肢の量が多いため、その直前に入力した項目の値によって、動的に絞り込みたいというご要望でした。

実は、旧バージョンのフォームのカスタマイズではこれをやろうとすればできました。

上にご紹介した従来フォームのカスタマイズリファレンスにある、”fb.events.kviewer.records.fetch”というイベントリスナーを使えば、選択肢の中をループして、それぞれの選択肢ごとに表示するしないを取捨選択できます。つまり、その中で不要なものを除外すれば要件は満たせました。

ところが、新フォームではこの機能がないのです。リファレンスも見ましたし、ブラウザの開発者モードでも探しました。トヨクモ社のサポートにも問い合わせました。でも、ないものはないのです。

だからといって、絞りたい種類の数だけフォームを作るのは非現実的です。また、そもそも運用に無理があるのでは、ということも検討しました。ただ、お客様の扱われるサービスの種類とフォームを扱う消費者様の利便性を秤にかけた結果、これは新フォームでは無理、という結論に達しました。

5.急遽Contact Form 7+Form Data to kintoneに

  Topへ↑

FormBridgeを諦めるほかありませんでした。ただ、諦めたのは該当するフォームだけです。
お客様はほかにも多くのフォームを作って運営しているため、FormBridgeから他のサービスに切り替える選択肢はありませんでした。
また、下に記すほかの理由でも、FormBridgeから動かすことはできませんでした。
では、どうするか。
Contact Form 7です。
Contact Form 7はCMS(Contents Management System)で有名なWordPressで動作する、フリーのwebフォーム作成用の拡張機能です。
というわけで、一つのフォームのために、WordPressを入れていただくことになりました。
Contact Form 7であればカスタマイズもできるので、ご要望には対応できるはず。

なお、Contact Form 7と同じくWordPressの著名なWebフォームの拡張機能に”MW WP Form”がありました。
ありました、と書いたのは、すでに開発終了しているからです。
以下のページにもその旨が記載されています。
kintoneとの連携プラグインもあり、うちも実装経験もあり、改修にも少し関わっていたのですが残念です。

また、Contact Form 7を入れただけでは、フォームに入力したデータをkintoneに登録することはできません。
そこで登場するのはForm data to kintoneです。
拡張機能ページ

これは関西の細谷さんが作ったお役立ち拡張機能であり、私も何度かお世話になっています。
Contact Form 7を入れるなら、何はともあれForm Data to kintone。ごはんとみそ汁。カレーライスと福神漬けのように鉄板の組み合わせです。

6.マルチステップフォームも実装

  Topへ↑

ところが、FormBridgeではステップフォームを使っていました。

ステップフォームとは、一画面のフォームではなく、複数ページにまたがるフォームを作成することができる機能です。(ステップフォームヘルプぺージ)

おっと!Contact Form 7にはこのマルチフォームの機能がないのです。ついにカスタマイズ?

いえ、まだContact Form 7 Multi-Step Formsがあります。これも拡張機能です。

拡張機能ページ
こちらを使えば、多少設定方法を覚える必要があり、少々の手間がかかりますが、ステップフォームが実装できなくもないです。

ただし、ここでついにカスタマイズが必要になってしまうのです。

というのも、このContact Form 7 Multi-Step Formsにはプライバシー設定上でまずい問題があるのです。

この問題について解説した記事

これはちょっとまずいので、カスタマイズに手を染めねばなりません。

ここまで順調にノーコード人生on the runだったのに、とうとう節を曲げなければならず、ちょっと残念。

7.一フォームから複数アプリへの同時書き込み

  Topへ↑

むぅぅぅ。これは困った。ついにカスタマイズに手を染めてしまいました。

さらに、課題は襲い掛かってきます。

FormBridgeの件のフォームでは、回答保存プロセスを用いて、複数のアプリへのデータ登録を行っていました。

つまり、一フォームで入力したその結果を同時に複数のアプリに書き込む必要があるのです。

この機能はFormBridgeだけであり、同様の他のサービスではできないはずです。少なくとも私は知りません。

さらにいうと、この同時に複数のアプリに書き込む機能はContact Form 7+Form Data to kintoneにもありません。

ここで二度目のカスタマイズ。一度きりよ♪と手を切ったはずが、再びカスタマイズに手を染めるはめになるとは。

仕方なく、phpで実装しました。

なお、phpを知らない方にお伝えすると、phpとは、Webシステムでよく使われている言語です。WordPressもContact Form 7もForm Data to kintoneもContact Form 7 Multi-Step Formsもphpを使って実装されています。

今回、一連の仕組みはWordPressで動くとお伝えしました。

phpを用いて機能を拡張するには、WordPressの仕組みを利用します。

具体的には、どういう風に実装するかというと、アクションフックという仕組みを使います。

これは、WordPress本体またはプラグインが実装する「あるタイミングで任意の処理を実行する」処理を組み込める機能です。

Form Data to kintoneにもアクションフックが用意されています。

“form_data_to_kintone_completed_saving”というアクションフックを使えば、Form Data to kintoneが正常に保存したタイミングで、任意の処理を行うよう実装できます。

任意の処理とは、以下のコードでいうと、send_to_multiple_kintone_appsの関数です。この関数が実行されます。

つまり。一つめのアプリへの書き込みはForm data to kintoneで行い、二つ目のアプリ以降は以下のphpを用いて追加ないしは更新ができるように改修しました(以下のプログラムではログやエラー処理や更新処理はすべて省いています。)。

なお、この記事の内容の文責は私にあります。くれぐれも細谷さんに質問はしないようにお願いしますね^_^。

 'create', // ★追加: 処理タイプ
            'subdomain' => 'hogehoge',
            'app_id'    => '8888',
            'api_token' => 'yuutokukedokoredetaramenatokunyade',
            'fields'    => array(
                'なんとか_ID'    => $guid,
                'かんとか_ID'    => '5555',
                '氏名'      => '長井',
            )
        ),
    );

    // 各アプリにデータを送信
    foreach ($additional_apps as $app_config) {
        if (!isset($app_config['type'])) {
            $app_config['type'] = 'create'; // デフォルトは新規登録
        }
        
        if ($app_config['type'] === 'create') {
            send_to_kintone_app($app_config, $posted_data);
        } elseif ($app_config['type'] === 'update') {
            update_kintone_app_records($app_config, $posted_data); //こっちの中身は割愛します。
        }
    }
}

function send_to_kintone_app($app_config, $posted_data) {
    $url = sprintf(
        'https://%s.cybozu.com/k/v1/record.json',
        $app_config['subdomain']
    ); // リクエストurlの組み立て

    $record = array();
    foreach ($app_config['fields'] as $kintone_field => $value) {
        if (!isset($posted_data[$value])) {
            if (is_array($value)) {
                $record[$kintone_field] = array('value' => $value);
            } else {
                $record[$kintone_field] = array('value' => $value);
            }
        } else {
            $field_value = $posted_data[$value];
            if (is_array($field_value)) {
                $record[$kintone_field] = array('value' => $field_value);
            } else {
                $record[$kintone_field] = array('value' => $field_value);
            }
        }
    }

    $body = array(
        'app'    => $app_config['app_id'],
        'record' => $record
    ); // rest api のボディ

    $args = array(
        'method'  => 'POST',
        'headers' => array(
            'X-Cybozu-API-Token' => $app_config['api_token'],
            'Content-Type'       => 'application/json',
        ),
        'body'    => json_encode($body),
        'timeout' => 30,
    ); // ヘッダ

    $response = wp_remote_post($url, $args);

    if (is_wp_error($response)) {
        error_log(sprintf(
            '[Kintone Multi App] アプリID %s への送信失敗: %s',
            $app_config['app_id'],
            $response->get_error_message()
        ));
    } else {
        $response_code = wp_remote_retrieve_response_code($response); // 受け取ったデータに応じて結果を処理
    }

    return $response;
}
?>

詳しくは語りませんが、実は上の処理は弊社の標準処理ではありません。

phpはすべてクラスモジュール化し、基底クラスと派生クラスに分けて実装しています。つまり上のプログラムは実物ではないことをお伝えしておきます。

実は、まだ問題があるのです。

まず、フォームを表示したタイミングで任意の項目に値を含める必要があります。

そもそも、今回の嫌々カスタマイズに手を染めた理由は、kViewerで動的に選択肢を絞りたいからでした。

kViewerに似た機能、つまりフォームを表示した時点で任意のアプリからデータを取得しなければならないのです。

まずそれは先ほどと同じくfunctions.phpで

 array(
            'X-Cybozu-API-Token' => $token
        ),
        'timeout' => 30
    ));
    
    $status_code = wp_remote_retrieve_response_code($response);
    $body = wp_remote_retrieve_body($response);
    
    $data = json_decode($body, true);
       
    return isset($data['records']) ? $data['records'] : array();
}

$***** = get_kintone_records(KINTONE_*****, KINTONE_API_TOKEN_*****);
$**** = get_kintone_records(KINTONE_APP_****, KINTONE_API_TOKEN_****);
$*** = get_kintone_records(KINTONE_APP_***, KINTONE_API_TOKEN_***);

// データを整形
$*****_list = array();
$*****_id_map = array();
$*****_name_en_map = array();

if (!empty($*****) && is_array($*****)) {
    foreach ($***** as $record) {
        if (isset($record['レコード番号']['value']) && isset($record['***']['value'])) {
            $*****_record_number = $record['レコード番号']['value'];
            $*****_name = $record['***']['value'];
            $****_id = isset($record['***_ID']['value']) ? $record['***_ID']['value'] : $*****_record_number;
            $****_name_en = isset($record['*****_**']['value']) ? $record['*****_**']['value'] : '';
            
            if (!empty($*****_name)) {
                $*****_list[] = array(
                    'id' => $****_id,
                    'name' => $*****_name,
                    'name_en' => $****_name_en,
                    'record_number' => $*****_record_number
                );
                
                $*****_id_map[$****_id] = $*****_record_number;
                
                if (!empty($****_name_en)) {
                    $*****_name_en_map[$****_name_en] = $****_id;
                }
            }
        }
    }
}

?>

というプログラムで、任意のアプリからデータを取得します。

これも先ほどと同じくプログラム上ではログやエラー処理や更新処理はすべて省いています。また、弊社の標準処理ではないことはお断りしておきます。

実はまだあるのです。

え?まだカスタマイズあるの?

という嘆きが聞こえてきそうです。

思い出してください。今回はある値に応じて選択肢の選択肢を絞りたい、ということでしたよね。

そう、つまり画面上で動的に値を絞り込む処理があります。

    // JavaScriptファイルを読み込む
    wp_enqueue_script(
        'cf7-kintone-dynamic',
        get_stylesheet_directory_uri() . '/js/cf7-kintone-dynamic.js',
        array('jquery'),
        '1.0.9',
        true
    );
    
    // データをJavaScriptに渡す
    $js_data = array(
        '****' => $*****_list,
        '****' => $****_list,
        '***' => $***_list,
        '**' => $**_list,
        'currentDate' => current_time('Y-m-d'),
        'urlParams' => array(
            '****Id' => $resolved_****_id,
            '*******Id' => $resolved_*******_id,
            '***Id' => $resolved_***_id,
            'original****NameEn' => $url_****_name_en,
            'original*******Code' => $url_*******_code
        )
    );
    
    wp_localize_script('cf7-kintone-dynamic', 'kintoneData', $js_data);

はい、ここで上げた三つのコードを最終的に

add_action(‘wp_enqueue_scripts’, ‘enqueue_cf7_kintone_script’);
で呼び出す必要があります。これは先ほどとは違い、WordPress本体が提供するアクションフックです。

もう一つ、JavaScriptも変えねばなりません。

    const **** = kintoneData.**** || [];
    const ******* = kintoneData.******* || {};
    const *** = kintoneData.*** || {};
    const ** = kintoneData.** || {};
    const currentDate = new Date(kintoneData.currentDate);
    
    // URLパラメータを取得
    const urlParams = kintoneData.urlParams || {};
    const url****Id = urlParams.****Id || '';
    const url*******Id = urlParams.*******Id || '';
    const url***Id = urlParams.***Id || '';
        
    // 選択状態を保持
    let selected**** = null;
    let selected******* = null;
    let selected*******Data = null;
    let selected*** = null;
    let **Count = 1;
    let member****Code = '';
    let startDate = null;
    
    function initialize****Select() {
        const $****Select = $('#select-****');
        $****Select.empty();
        $****Select.append('');
        
        ****.forEach(function(****) {
            $****Select.append($('');
        
        if (****Id && *******[****d]) {
            *******[****Id].forEach(function(*******) {
                $*******Select.append($('

このJavaScriptファイルは先ほどのfunctions.phpと同じくテーマ/子テーマフォルダ直下におきます。
今回はテーマ/子テーマフォルダ直下にjsというフォルダを作り、その中に入れています。

さて、これらのJavaScriptファイルやphpファイルは、ftpまたはsftpまたはscp(sslを用いてファイル送信をするコマンド)を使ってアップロードしなければなりません。

8.結論!ノーコードで作れるFormBridgeってええわぁ

  Topへ↑

さて、プログラムがよくわからない方はすでに前の章を読み飛ばしたか、そもそも離脱してこの文章を読んでいないかもしれません。

でも、これでもまだほんの一部です。

細かい処理はほかにも端折ってここに紹介しています。

例えば、
・urlの値による処理の切り分けをJavaScriptで実装しています。
・Contact Form 7の処理を無視して、選択肢に値を入れているので、Contact Form 7のデフォルトで利いているエラー処理に引っかからないための処理を迂回するロジックも整備する必要がありました。

どうでしょう。ここまで読み進められた方は、これだけのコーディングを行ってフォームを作る道を選ぶのでしょうか。
それともノーコードで簡潔に設定だけで済ませられるFormBridgeのようなフォームを選ぶのでしょうか。
選択するのはあなた自身です。

ただし、あなた自身がコーディングを、いばらの道を選んだとしても、お客様にとってはどうでしょうか。

簡単にノーコードで設定が終わるのに、余分な費用と時間をかけて実装を行うことをお客様は良しとしますか?

また、自社の上司は? 余分な費用と手間がかかることで、その分を請求額に乗せ、かつ失注のリスクを引き受けるのでしょうか?

もちろん、他に選択肢がなかった頃なら、問答無用でコーディングに手を染めるしかありませんでした。

でも、今はノーコードツールが多数あります。FormBridgeやkintoneのような。

こうした文明の利器を使わずに、カスタマイズに手を染めることは、顧客にとって最善ではないばかりか、自社にとっても工数増、リスク増のデメリットをもたらすのです。

今回は、FormBridgeの新フォームでkViewerルックアップの選択肢の動的な絞り込みができないため、

やむを得ずカスタマイズに手を染めました。

でも、もしノーコードでできるのに敢えてカスタマイズに手を染めるとすれば、それは大いなる損失です。

しかもほかに多くの案件が来ている中、多くの作業が積み上がり続ける中、あえて時間と手間と工数のかかるカスタマイズを選ぶなら、よほどの理由がないと納得してもらえないでしょう。

FormBridgeの旧フォームでは使えた、「kViewerルックアップの動的な選択肢絞り込みのためのJavaScriptカスタマイズ」を実装可能にする手段は、とりあえずトヨクモさんの実装を待ちましょう!

9.まとめ

  Topへ↑

 プログラミングは楽しい。でも、それはノーコードツールでできる代替案があるのなら、生産性を大きく下げる営みです。
 プログラミングを行うなら、個人的な趣味として続けるべきかもしれません。

 一方で、今はAIが優れたプログラムを書いてくれるようになっています。それらを用いれば、ノーコードツールに頼らなくても、AIが書いてくれるコードを用いれば、うまくいくかもしれません。
 ただ、それ一度決まった仕様が長らく使える場合には有効ですが、組織変更や取扱商品変更、運用フローの変更などでソースコードを何度も修正する可能性があるのなら、AIを使ったとしても、手間が生じます。
 また、引継ぎのリスクもあります。後任者が果たしてコードを見るだけで修正がかけられるものか。AIにおせっかいを焼かれて余計なソースを紛れ込まされたりしないか。そうした指示が完璧にできるようなプロンプトを前任者から後任者に引き継げるか。
 AI利用にはこのようなリスクがあることも忘れてはなりません。

私としては、コーディングは趣味の世界に限定することが推奨され、ビジネス現場ではコーディングが禁止される日もそう遠くないとみています。
すでにポリシーでkintoneのJavaScriptコーディング禁止の企業がいくつもあるように。

もし、ノーコードツールの良さに懐疑的、または学習コストを嫌がってノーコードツールを使わない技術者がいるなら、早めに今までのやり方から変えた方がよいかもですよ!

ね!最後はディスるのではなく、きちんとFormBridgeの良さを伝えられたでしょ?

コメント

タイトルとURLをコピーしました