LaravelとGASを連携してお問い合わせ機能を実装する方法

LaravelとGASを連携してお問い合わせ機能を実装する方法

皆さん、こんにちは!

今回はGASをAPIとしてお問い合わせ機能を実装する方法についてご紹介したいと思います。

GASとは、「Google Apps Script」のことで、Googleサービスを連携して業務を自動化したり拡張したりすることができるものです。

LaravelとGASを使ってお問い合わせ機能を実装してみたので、その連携方法やGASの使い方などを紹介していきます。

実装

では早速実装を見ていきたいと思いますが、フロントエンドの実装は今回は省略します。

GAS

GASのソースコードは以下です。

function doPost(e) {
  // --- 簡易認証の設定 ---
  var SECRET_TOKEN = "××××××××××××××";
  
  if (e.parameter.token !== SECRET_TOKEN) {
    return ContentService.createTextOutput("Unauthorized").setMimeType(ContentService.MimeType.TEXT);
  }
  // --------------------

  var name    = e.parameter.name;
  var email   = e.parameter.email;
  var message = e.parameter.message;
  
  var recipient = "{GMAIL_ADDRESS}";
  var subject = "【サイトからのお問い合わせ】" + name + "様より";
  
  var body = "名前: " + name + "\n" +
             "メール: " + email + "\n\n" +
             "内容:\n" + message;

  try {
    MailApp.sendEmail(recipient, subject, body);
    return ContentService.createTextOutput("success");
  } catch (err) {
    return ContentService.createTextOutput("error: " + err.toString());
  }
}

エンドポイントが漏洩すると他の人でもAPIを実行することができてしまうため、トークンを使って最低限の認証機能をつけています。また、{GMAIL_ADDRESS}のところに送信先のGmailアドレスを入れます。

あとは、「デプロイ」のボタンからウェブアプリとしてデプロイするだけです。エンドポイントURLとトークンは後ほどLaravel側の実装に使うため、控えておきます。

Laravel

Laravel側は以下のようなコードになります。

<?php

...

use App\Http\Requests\ContactRequest;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Log;

...

    /**
     * Send the contact email.
     */
    public function send(ContactRequest $request)
    {
        try {
            // 設定値のバリデーション
            $webAppUrl = config('services.gas.web_app_url');
            $secretToken = config('services.gas.secret_token');

            if (empty($webAppUrl) || empty($secretToken)) {
                Log::error('GAS configuration is missing or empty', [
                    'web_app_url_set' => !empty($webAppUrl),
                    'secret_token_set' => !empty($secretToken),
                ]);

                return redirect()
                    ->route('contact')
                    ->with('error', 'メールの送信に失敗しました。しばらくしてから再度お試しください。')
                    ->withInput();
            }

            // GASにHTTP POSTリクエストで送信
            $response = Http::timeout(30)->asForm()->post($webAppUrl, [
                'token' => $secretToken,
                'name' => $request->name,
                'email' => $request->email,
                'message' => $request->message,
            ]);

            if ($response->successful()) {
                return redirect()
                    ->route('contact')
                    ->with('success', 'お問い合わせを送信しました。ありがとうございます。');
            } else {
                Log::error('GAS contact form submission failed', [
                    'status' => $response->status(),
                ]);

                return redirect()
                    ->route('contact')
                    ->with('error', 'メールの送信に失敗しました。しばらくしてから再度お試しください。')
                    ->withInput();
            }
        } catch (\Exception $e) {
            Log::error('Contact form error', [
                'message' => $e->getMessage(),
                'trace' => $e->getTraceAsString(),
            ]);

            return redirect()
                ->route('contact')
                ->with('error', 'メールの送信に失敗しました。しばらくしてから再度お試しください。')
                ->withInput();
        }
    }
}

エンドポイントURLとトークンは漏洩しないように、シークレット環境変数として管理し、ログにも出さないように気をつけましょう。

背景

Laravelでメール送信機能を実装する場合、通常はLaravelのMailableクラスを使うか、SendGridやMailgunなど外部サービスを使ってメール送信機能を実装するのが普通かと思います。ただ、今回の自分のケースでは以下の制約があり、GASを使ったお問い合わせ機能の実装方法を選択してみました。

  • Gmailにメール送信したかったが、VPS(サーバー)側でGmailで使う587ポートがネットワーク層でブロックしていて使えなかった。
  • SendGridやMailgunなどを使う選択肢もあったが、メール送信機能自体はお問い合わせぐらいでしか使わない予定のため、もっと簡易的に実装したかった。
  • 最も簡易的に実装するにはGoogleフォームを使う選択肢もあったが、お問い合わせだけ外部URL&デザインが浮いてしまうのは避けたかった。

つまり、SendGridやMailgunを使うほどでもないが、Googleフォームは簡易的すぎて嫌だったわけですね笑。というわけで、デザインは既存サイトとの一貫性を保ちつつ、裏側はGASをAPI的に使いお手軽にお問い合わせ機能を実装する方針にしました。

ただし、GASで実装する方法はメリットばかりではなくデメリットもあるので、きちんとメリデメを理解して適切なケースで使いましょう。メリデメをまとめると、以下のような感じだと思います。

メリット デメリット
無料で使える 無料枠(1日100通程度)を超える大量送信には不向き
実装が簡単 セキュリティ面では外部サービスに劣る
Googleのサーバーから送るため、Gmail宛への到達率が非常に高い 送信元がGASを紐付けたGoogleアカウントに固定される
サーバー管理が不要 エラー系のステータスコードは基本返せない
HTTPSで通信するため、基本的にポートがブロックされる心配がない GAS側のコードを更新するたびに再デプロイが必要になるため、エンドポイントURLをセットしなおす必要がある

個人的には、実装が簡単な一方でGASのコードを更新するたびにエンドポイントをシークレット環境変数にセットしなおす必要があるのが結構ネックかなと思いました。

今後の展開としては、ひとまず今の実装で様子を見て、必要であれば段階的にSendGridやMailgunなど外部サービスを使った実装方法に移行していきたいと思います。