【Laravel】お問い合わせフォーム【Sendgrid】

# 0. はじめに
大阪のLaravel初学者サウナーこと、kazumakishimoto([@kazuma_dev](https://twitter.com/kazuma_dev))です！
`Sendgrid`でLaravelのお問い合わせフォームの作り方です！

## 0-1. 全体の流れ
> [1.Sendgrid](#1-sendgrid)
> [2.Laravel](#2-laravel)
> [補足](#補足)
> [Reference](#reference)

## 0-2. 本記事の対象者
- SendgridでLaravelのお問い合わせフォームを作成したい方

## 0-3. 事前準備
- Sendgridアカウント
- Gmailアカウント

https://www.google.co.jp/mail/

## 0-4. 要件
- Sendgrid登録
- Sendgridを.env記載
- Laravel実装

## 0-5. 使用画像のイメージ
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/687994/e96e5da5-ffa8-107e-ac42-d04fd9c86fbe.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/687994/264e891b-d76f-9321-09b7-0d4b9f27ce2b.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/687994/d53bce1e-24c2-85ae-8a29-80f322573660.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/687994/d96f9349-bc3c-0ff4-34ca-0bce48a8ef0d.png)


# 1. Sendgrid
- 下記参考にAPIKEY作成(※編集中)

https://qiita.com/d0ne1s/items/4bc26378c1eb7f9a19cc

https://www.techpit.jp/courses/11

# 2. Laravel
## 2-1. .env
```
# Sendgrid
MAIL_DRIVER=smtp
MAIL_HOST=smtp.sendgrid.net
MAIL_PORT=587
MAIL_USERNAME=apikey
MAIL_PASSWORD=SG.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
MAIL_ENCRYPTION=tls
MAIL_FROM_NAME="${APP_NAME}"
MAIL_FROM_ADDRESS=hoge@gmail.com
```

## 2-2. Route
```php:routes/web.php
# お問い合わせ
// 入力ページ
Route::get('/contact', 'ContactController@index')->name('contact.index');
// 確認ページ
Route::post('/contact/confirm', 'ContactController@confirm')->name('contact.confirm');
// 送信完了ページ
Route::post('/contact/thanks', 'ContactController@send')->name('contact.send');
```

## 2-3. Controller
```php:app/Http/Controllers/ContactController.php
<?php

namespace App\Http\Controllers;

use App\Http\Requests\ContactRequest;
use App\Mail\ContactSendmail;

class ContactController extends Controller
{
    public function index()
    {
        return view('contact.index');
    }

    public function confirm(ContactRequest $request)
    {
        $inputs = $request->all();

        $data = [
            'inputs' => $inputs,
        ];
        return view('contact.confirm', $data);
    }

    public function send(ContactRequest $request)
    {
        $inputs = $request->all();

        if ($request->has("back")) {
            return redirect()->route('contact.index')
                ->withInput($inputs);
        }

        \Mail::to('grfl.official@gmail.com')->send(new ContactSendmail($inputs));
        $request->session()->regenerateToken();
        return view('contact.thanks');
    }
}
```

## 2-4. ContactRequest
```php:app/Http/Requests/ContactRequest.php
<?php

namespace App\Http\Requests;

use Illuminate\Foundation\Http\FormRequest;

class ContactRequest extends FormRequest
{
    /**
     * Determine if the user is authorized to make this request.
     *
     * @return bool
     */
    public function authorize()
    {
        return true;
    }

    /**
     * Get the validation rules that apply to the request.
     *
     * @return array
     */
    public function rules()
    {
        return [
            'name'  => 'required|string|max:15',
            'email' => 'required|email:strict,dns|max:255',
            'title' => 'nullable|string|max:255',
            'body'  => 'required|string|max:1000',
        ];
    }

    public function attributes()
    {
        return [
            'name' => 'お名前',
            'email' => 'メールアドレス',
            'title' => '件名',
            'body' => 'お問い合わせ内容',
        ];
    }
}
```

## 2-5. View
### 2-5-1. index.blade.php
```php:resources/views/contact/index.blade.php
@extends('app')

@section('title', 'お問い合わせ - grfl')

@section('content')

@include('nav')
<header>
    <div>
        <div class="container" style="max-width: 900px;">
            <nav aria-label="breadcrumb">
                <ol class="breadcrumb bg-white small pl-0 mb-0">
                    <li class="breadcrumb-item">
                        <a href="/" class="text-teal1">grfl</a>
                    </li>
                    <li class="breadcrumb-item active" aria-current="page">
                        お問い合わせ
                    </li>
                </ol>
            </nav>
        </div>
    </div>
</header>

<main>
    <div class="bg-paper my-3">
        <div class="container p-0" style="max-width: 540px">
            <h4 class="text-center">
                    お問い合わせ
                </h4>
            <p style="font-size: 14px;">
                    grflに対するご意見・ご感想・お問い合わせなどございましたらお聞かせください。<br>
                    お送りいただいた内容はすべて確認しておりますが、ご返信を差し上げることができない場合もございますので、ご了承ください。
                </p>
            @if (session('status'))
            <div class="card-text alert alert-success">
                {{ session('status') }}
            </div>
            @endif
            <div class="card shadow-sm mb-4">
                <div class="card-body">
                    @include('error_card_list')
                    <form method="post" action="{{ route('contact.confirm') }}">
                        @csrf
                        <p class="small">
                                (<span class="text-danger">*</span>は必須項目です)
                            </p>
                        <div class="form-group">
                            <label for="name">お名前</label>
                            <span class="text-danger">*</span>
                            <input class="form-control" type="text" id="name" name="name" required value="{{ Auth::user()->name ?? old('name') }}">
                        </div>
                        <div class="form-group">
                            <label for="email">メールアドレス</label>
                            <span class="text-danger">*</span>
                            <input class="form-control" type="email" id="email" name="email" required value="{{ Auth::user()->email ?? old('email') }}">
                        </div>
                        <div class="form-group">
                            <label for="title">ご用件</label>
                            <input class="form-control" type="title" id="title" name="title" value="{{ old('title') }}">
                        </div>
                        <div class="form-group">
                            <label for="body">お問い合わせ内容</label>
                            <span class="text-danger">*</span>
                            <textarea type="text" name="body" id="body" rows="5" class="form-control" required>{{ old('body') }}</textarea>
                            <p class="text-muted small ml-1">1000文字以内</p>
                        </div>
                        <label for="agree" class="small" role="button">
                            <span class="d-flex flex-wrap">
                                <span>
                                    <input type="checkbox" id="agree" required>
                                    <a href="{{ route('privacy') }}" class="text-teal1 ml-2" target="_blank" title="プライバシーポリシーをブラウザの別画面で開く">プライバシーポリシー</a>
                                    <span>を確認し、</span>
                                </span>
                                <span>同意</span>
                                <span>
                                    <span>
                                        <span>しました。</span><span class="text-danger">*</span>
                                    </span>
                                </span>
                            </span>
                        </label>
                        <button type="submit" class="btn btn-block bg-white btn-outline-teal1 text-decoration-none text-teal1 mt-4">
                            <b>送信内容を確認する</b>
                        </button>
                    </form>
                </div>
            </div>
        </div>
    </div>
</main>

@include('footer')

@endsection
```

### 2-5-2. confirm.blade.php
```php:resources/views/contact/confirm.blade.php
@extends('app')

@section('title', 'お問い合わせ内容の確認 - grfl')

@section('content')

@include('nav')

<main>
    <div class="bg-paper my-3">
        <div class="container p-0" style="max-width: 540px">
            <h4 class="text-center">
                    お問い合わせ内容の確認
                </h4>
            <div class="card shadow-sm mb-4">
                <div class="card-body">
                    <form method="post" action="{{ route('contact.send') }}">
                        @csrf
                        <div class="form-group">
                            <label for="name" class="text-muted">
                                お名前
                            </label>
                            <p>
                                    {{ $inputs['name'] }}
                                </p>
                            <input name="name" value="{{ $inputs['name'] }}" type="hidden">
                        </div>
                        <div class="form-group">
                            <label for="email" class="text-muted">
                                メールアドレス
                            </label>
                            <p>
                                    {{ $inputs['email'] }}
                                </p>
                            <input name="email" value="{{ $inputs['email'] }}" type="hidden">
                        </div>
                        <div class="form-group">
                            <label for="title" class="text-muted">
                                ご用件
                            </label>
                            <p>
                                    @if ($inputs['title'] !== null)
                                        {{ $inputs['title'] }}
                                    @else
                                        無題
                                    @endif
                                </p>
                            <input name="title" value="{{ $inputs['title'] }}" type="hidden">
                        </div>
                        <div class="form-group">
                            <label for="body" class="text-muted">
                                お問い合わせ内容
                            </label>
                            <p>
                                    {!! nl2br(e($inputs['body'], false)) !!}
                                </p>
                            <input name="body" value="{{ $inputs['body'] }}" type="hidden">
                        </div>
                        <button type="submit" name="back" value="back" class="btn btn-block btn-teal1 mt-4">
                            入力内容修正
                        </button>
                        <button type="submit" name="action" value="submit" class="btn btn-block bg-white btn-outline-secondary text-decoration-none text-secondary mt-4">
                            送信する
                        </button>
                    </form>
                </div>
            </div>
        </div>
    </div>
</main>

@include('footer')

@endsection
```

### 2-5-3. thanks.blade.php
```php:resources/views/contact/thanks.blade.php
@extends('app')

@section('title', 'お問い合わせメール送信完了- grfl')

@section('content')

@include('nav')

<main>
    <div class="bg-paper my-3">
        <div class="container p-0" style="max-width: 540px">
            <h4 class="text-center">お問い合わせ:送信完了</h4>
            <div class="alert alert-info" role="alert">
                <h5>
                        お問い合わせメールが送信されました!
                    </h5>
                <a class="text-decoration-none text-info" href="/">
                    ホームへ戻る<i class="fas fa-chevron-circle-right ml-1"></i>
                </a>
            </div>
        </div>
    </div>
</main>

@include('footer')

@endsection
```

### 2-5-4. mail.blade.php
```php:resources/views/contact/mail.blade.php
お問い合わせメールを受け付けました。<br>
<br>
■お名前<br>
{!! $name !!}<br>
<br>
■メールアドレス<br>
{!! $email !!}<br>
<br>
■タイトル<br>
@if ($title !== null)
{!! $title !!}<br>
@else
無題<br>
@endif
<br>

■お問い合わせ内容<br>
{!! nl2br($body) !!}<br>
```


# 補足
## 開発環境（FW/ツールのバージョンなど）
|ツール|バージョン|
|:--|:--|
|Vue.js|2.6.14|
|jQuery|3.4.1|
|PHP|7.4.1|
|Laravel|6.20.43|
|MySQL|5.7.36|
|Nginx|1.18.0|
|Composer|2.0.14|
|npm|6.14.6|
|Git|2.33.1|
|Docker|20.10.11|
|docker-compose|v2.2.1|
|PHPUnit|8.0|
|CircleCI|2.1|
|heroku|7.59.4|
|MacBook Air|M1,2020|
|macOS|Monterey 12.3|
|Homebrew|3.3.8|

## ディレクトリ構造
```
【ルートディレクトリ】
├─ .circleci
│   └─ config.yml
├─ aws / CloudFormation
│   └─ ec2.yml
├─ docker
│   └─ mysql
│   └─ nginx
│   └─ php
│   └─ phpmyadmin
├─ src
│   └─ 【Laravelのパッケージ】
└─ docker-compose.yml
```

# Reference
https://www.techpit.jp/courses/11

https://qiita.com/d0ne1s/items/4bc26378c1eb7f9a19cc

https://qiita.com/kt103/items/d6f6f5fd830cc791db17

https://qiita.com/ysg/items/413458323439ea372597