2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

Laravel、ajaxを用いて、非同期のメール送信APIを作成し、問い合わせ内容をgmailで送信する。

Last updated at Posted at 2023-06-10

目次

1. 概要
2. 追加機能の概要
3. ルート設定
4. メール送信クラスの作成
5. メール送信APIの作成
6. InquirySendMailメイラブルクラス編集
7. メール文面のビューの作成
8. APIで返却したメッセージを画面表示させる
9. おわりに

1. 概要

問い合わせフォームから、
問い合わせ内容をgmailで送信するロジックを説明します。

2. 追加機能の概要

  • お問い合わせページを追加

    • ページURLは、/contact
    • お名前、メールアドレス、タイトル、お問い合わせ内容を入力。
    • SUBMITボタンを押す事で、ユーザーにgmail送信する。
  • お問い合わせ内容をgmailで送信(API)

    • ポストメソッドの宛先URLは、/contact/thanks とする。
    • InquiryController内で処理する。
    • データベースへの保存はしない。
    • メールを、管理者と問い合わせした人に送信する。

3. ルート設定

Route::controller(InquiryController::class)->group(function() {
    Route::get('/contact', 'index');
});

対象ファイルは、 routes\web.php
/contact にGETメソッドでアクセスすると InquiryController.phpのindex()メソッドを呼び出し、お問い合わせ画面が表示される。

Route::controller(InquiryController::class)->group(function() {
    Route::post('/contact/thanks', 'store');
});

対象ファイルは、 routes\api.php
同様に、/contact/thanks にPOSTメソッドでアクセス(SUBMITボタン押下)すると、同じInquiryController.phpのstore()メソッドを呼び出す。

4. メール送信クラスの作成

laravelではメール送信するためにMailable(メイラブル)クラスを作成する。
Mailableとメール送信の方法について詳しくは以下参照。

メール 9.x Laravel

Mailableクラスは、artisan makeコマンドで作成できる。

php artisan make:mail InquirySendMail

対象 app\Mail\InquirySendMail.php
→このファイルとMailファサードのtoメソッド使って、メールを送信する。

設定ファイルにメールサーバーの情報を入力する。

gmailのメールアカウントとSMTPサーバーを使ってメールを送信する。
gmailでメール送信するため、以下手順が必要。

  1. gmailで2段階認証の設定。
  2. アプリパスワードの発行。
  3. SMTPサーバーの設定を.envに追加。
    (参考:https://qiita.com/hiro5963/items/df062ab19e8ceba4573f)
MAIL_MAILER=smtp
MAIL_HOST=smtp.gmail.com
MAIL_PORT=587
MAIL_USERNAME=admin@gmail.com // 自身のgmailを設定。
MAIL_PASSWORD=アプリパスワード // 2で作成したアプリパスワードを記載。
MAIL_ENCRYPTION=tls
MAIL_FROM_ADDRESS="admin@gmail.com" // 自身のgmailを設定。

→ローカル環境でgmail送信のテストを行いたい場合は、環境設定ファイル.env に上記を設定する。

5. メール送信APIの作成

対象は app\Http\Controllers\InquiryController.php 

use App\Mail\InquirySendMail;
use Illuminate\Support\Facades\Mail;

MailファサードとInquirySendMailメイラブルクラスを使用するので、use文を記述する。

storeメソッド

/**
* inquiry store
*
* @param Request $request
* @return \Illuminate\Http\Response
*/
public function store(Request $request)
{
    $request->validate([
        'name' => 'required',
        'email' => 'required|email',
    ]);

    Mail::send(new InquirySendmail($request));

    // 再送信を防ぐためにトークンを再発行
    $request->session()->regenerateToken();

    return response(
        [
            'result' => true,
            'message' => 'ありがとうございます。メッセージが送信されました。',
            'csrf_token' => $request->session()->token(),
        ],
    );
}
  1. api.phpに定義した、メール送信APIにて呼び出されるメソッド。
  2. お名前、メールアドレスは必須入力のバリデーションを指定。
  3. 入力データのバリデーションを行った後、MailファサードとInquirySendMailクラスを使用して、メールを送っている。
  4. Mail::の部分がMailファサード。(sendメソッドを呼び出している。)
  5. sendメソッドでは、InquirySendMailクラスで作成するメール内容を実際に送信している。
  6. $request->session()->regenerateToken();は、view側で作成したcsrf_tokenを再発行する事で、お問い合わせの2重送信を防いでいる。
    laravelは、csrf_token(クロス・サイト・リクエスト・フォージェリ(CSRF)から、アプリケーションを簡単に守ること) を発行する事が出来、Postリクエストを送る際は、以下のように「@csrf」を記載しないとエラーが発生する。
// metaタグにその値を保存しておく。
<meta name="csrf-token" content="{{ csrf_token() }}">

...略

<form method="POST">
    @csrf
    ...
</form>

6. InquirySendMailメイラブルクラス編集

全体コード

<?php

namespace App\Mail;

use Illuminate\Bus\Queueable;
use Illuminate\Mail\Mailable;
use Illuminate\Http\Request;
use Illuminate\Queue\SerializesModels;

class InquirySendmail extends Mailable
{
    use Queueable, SerializesModels;

    private $name;
    private $email;
    private $title;
    private $body;

    /**
     * Create a new message instance.
     *
     * @param Request $request
     * @return void
     */
    public function __construct(Request $request)
    {
        $this->name = $request->get('name');
        $this->email = $request->get('email');
        $this->title = $request->get('title');
        $this->body = $request->get('body');
    }

    /**
     * Build the message.
     *
     * @return $this
     */
    public function build()
    {
        $mail_admin = config('mail.from.address');

        return $this
            ->from($mail_admin, '自身の名前')
            ->to($this->email)
            ->bcc($mail_admin)
            ->subject('お問い合わせを受け付けました')
            ->text('inquiry.mail')
            ->with([
                'name' => $this->name,
                'email' => $this->email,
                'title' => $this->title,
                'body' => $this->body,
            ]);
    }
}

バリデート済みのデータを受け取るので、Requestクラスごと受け取る。

public function __construct(Request $request)
{
    $this->name = $request->get('name');
    $this->email = $request->get('email');
    $this->title = $request->get('title');
    $this->body = $request->get('body');
}

$this->が、このクラス内のデータで、constructメソッドの引数が受け渡される仮引数。

public function build()
{
    $mail_admin = config('mail.from.address');

    return $this
        ->from($mail_admin, '自身の名前')
        ->to($this->email)
        ->bcc($mail_admin)
        ->subject('お問い合わせを受け付けました')
        ->text('inquiry.mail')
        ->with([
            'name' => $this->name,
            'email' => $this->email,
            'title' => $this->title,
            'body' => $this->body,
        ]);
}

resources\views\mail\mail.blade.phpをビューとして指定する。

->演算子(アロー演算子)でつなげて、textメソッドで、ビューにデータを渡す。キーが’name’なのでviewの中で$nameという名前で参照することができる。

さらに、アロー演算子で subjectメソッドをつなげることで、メールの表題を設定することができる。

7. メール文面のビューの作成

メール文面は、 resources\views\mail\mail.blade.phpを指定。

{{ $name }} さま

お問い合わせいただき、
ありがとうございます。

【お問い合わせ内容】

お名前 : {{ $name }}

メール : {{ $email }}

タイトル : {{ $title }}

内 容 :
{!! $body !!}

$name等で渡されたデータを{{ }}内にて表示。

8. APIで返却したメッセージを画面表示させる。

  1. ajaxを用いて、非同期でメール送信APIを呼び出す。
    jqueryを使用する。 
    対象 app\public\js\contact_ajax.js

初期状態では、レスポンスメッセージ表示用クラスを非表示にしておく

$(function () {
    $(".response-output").hide();
    
    ....以下略

1行目の$(function(){では、即時関数を宣言している。.response-outputは、htmlの親要素。(後のajax処理で、子要素を追加する。)

送信ボタンが押された時だけ、API通信を開始する。

$(function () {
    $(".response-output").hide();

    $("#submit-button").click(function () {
        $(".response-output").hide();
        $(".success-response").remove();
        $(".error-response").remove();
        
....以下略

メッセージの画面表示有無に関わらず、最初にhide()で要素を隠し、removeで要素を削除する。
 →メッセージはjqueryのアニメーションでスライド表示させる為、hide()で隠す。
 →ボタン多重押下時に、メッセージが重複して表示されるのを防ぐための対応。

form入力データを取得し、変数に代入する

var name = $("input[name=your-name]").val();
var email = $("input[name=your-email]").val();
var title = $("input[name=your-title]").val();
var body = $("textarea[name=your-body]").val();

$.ajaxメソッドで通信を行う。

    $.ajax({
        headers: {
            "X-CSRF-TOKEN": $('meta[name="csrf-token"]').attr("content"),
        },
        type: "POST",
        dataType: "json",
        url: "api/contact/thanks",  
        timeout: 30000,
        data: { name: name, email: email, title: title, body: body }, 
    })
    .done((data) => {
            
    })
    .fail((data) => {
            
    });
  1. headersは、リクエスト時に送信するヘッダー。
    Ajax通信でも同様に、X-CSRF-TOKENを含めないと419エラーが発生するため、headerに含める必要がある。

  2. typeは通信に利用するHTTPメソッド(今回はメール送信なので、POST)

  3. dataTypeは、応答データの種類(レスポンスはjson型の為、’json’を指定)

  4. urlは、アクセス先のパス(inquiryController@storeメソッドにアクセスする。)

  5. timeoutは、指定秒数を経過しても通信が完了しない場合、エラー処理が実行される。

  6. dataは、inquiryController@storeメソッドに送信するデータ(form入力値)

  7. doneは、通信に成功した時に実行される。

  8. failは、通信に失敗した時に実行される。

API通信成功時、サクセスメッセージを表示させる

.done((data) => {
    $(".response-output")
        .append(
            '<p class="success-response">' + data.message + '</p>'
        )
        .slideDown();
})
  • dataはサーバーが返すデータで、お問い合わせAPIのレスポンスメッセージは、data.キーで取得できる。
  • data.messageresponse-outputというhtmlの親要素に、append()でレスポンスメッセージを表示させるための子要素を追加している。
  • 親要素は、hide()で隠しておいたので、.slideDown()で追加した子要素をスライド表示させる事ができる。

API通信失敗時、アラートメッセージを表示させる

.fail((data) => {
    var error_text = "入力内容に問題があります。確認して再度お試しください。";
    if (data.statusText == "timeout") {
        error_text = "通信に失敗しました。画面を更新して再度お試しください。";
    }
    $(".response-output")
        .append(
            '<p class="error-response">' + error_text + '</p>'
        )
        .slideDown();
    });
  • ajax通信が、30秒経っても成功しない場合は、error_textの中身を変更して親要素に追加する。

全体コード

$(function () {
    $(".response-output").hide();

    $("#submit-button").click(function () {
        $(".response-output").hide();
        $(".success-response").remove();
        $(".error-response").remove();

        var name = $("input[name=your-name]").val();
        var email = $("input[name=your-email]").val();
        var title = $("input[name=your-title]").val();
        var body = $("textarea[name=your-body]").val();
        
        $.ajax({
            headers: {
                "X-CSRF-TOKEN": $('meta[name="csrf-token"]').attr("content"),
            },
            type: "POST",
            dataType: "json",
            url: "api/contact/thanks",  
            timeout: 30000,
            data: { name: name, email: email, title: title, body: body }, 
        })
        .done((data) => {
            $(".response-output")
                .append(
                    '<p class="success-response">' + data.message + '</p>'
                )
                .slideDown();
        })
        .fail((data) => {
            var error_text = "入力内容に問題があります。確認して再度お試しください。";
            if (data.statusText == "timeout") {
                error_text = "通信に失敗しました。画面を更新して再度お試しください。";
            }
            $(".response-output")
                .append(
                    '<p class="error-response">' + error_text + '</p>'
                )
                .slideDown();
        });
    });
});

9. おわりに

上記手順で非同期にメール送信APIを実行できます。
今回、詳細なviewは記載していないので、お任せでお願いします。
環境によってうまく動かない場合があると思いますが、あくまでも自身のアウトプットのためにまとめており、誰かの参考程度になれば嬉しいな程度のため、ご理解いただけますと幸いです。

2
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?