[Rails]Action Mailerを使ってお問い合わせ機能でメールを送信する

#実装の目標

お問い合わせ機能をつくり、お問い合わせを送信したタイミングで管理者とお問い合わせしたユーザーの２名に確認メールを送信。（お問い合わせ内容はDBに保存されます）

この記事を読んでもらえると上記が実装できます！

#この記事の対象となる人

- お問い合わせ機能を実装したい人
- 送信時に確認メールを送りたい人
- 確認画面を挟んでから送信したい人

#開発環境

> ruby 2.6.3
> Rails 5.2.6

#前提

Gmailを使ってメールを送信しまので、Gmailの２段階認証とアプリパスワードの設定を行っておきましょう。
アプリパスワードはあとでまた必要になってくるのでコピペしておきましょう！(Gmailの設定方法については、記事内部にもリンク入れてるのであとで設定でもOK）

[２段階認証の設定方法](https://enjoypclife.net/google-account-gmail-2-step-verification/)
[アプリパスワード生成方法](https://www.howtonote.jp/google-account/2step-verify/index6.html)

`gem 'dotenv-rails'`で環境変数も使います。
モデル名、変数名等は適宜、ご自身の開発環境に変換してお考えください。

#モデル作成

今回はお問い合わせ内容をDBに保存するので、contactモデルを作っていきます。

```
$rails g model Contact
```

作られたマイグレーションファイルにカラムを追加していきます。

```db/migrate/2021~~_create_contacts.rb
class CreateContacts < ActiveRecord::Migration[5.2]
  def change
    create_table :contacts do |t|

      t.string :name     #名前カラム追加
      t.string :email    #メールアドレスカラム追加
      t.string :content  #お問い合わせ内容カラム追加

      t.timestamps
    end
  end
end
```

追加できたらモデルを作成します。

```
$rails db:migrate
```

contactモデルにバリデーションをかけていきます。
今回は全てに空のデータはだめ、名前は20文字以内、お問い合わせ内容は10文字以上でバリデーションをかけます。

```models/contact.rb
class Contact < ApplicationRecord
  validates :name, presence: true, length: { maximum: 20 }
  validates :email, presence: true
  validates :content, presence: true, length: { minimum: 10 }
end
```

#コントローラー作成

次にコントローラーをつくっていきます。
今回はビューは新規登録画面、確認画面、完了画面が必要なので、コントローラー作成と同時にビューも作っていきます。

```
$rails g controller contacts new confirm complete
```

これで、コントローラーとビューが作成されました。
コントローラーにcreateアクションを追加しておきます。
コントローラーの記述は順番にいきたいので、後述します。

```controllers/contacts_controller.rb
class ContactsController < ApplicationController
  def new
  end

  def confirm
  end
  
  # 追加
  def create
  end

  def complete
  end
end
```

#ルーティング設定

コントローラーを作ったときにgetルーティングが作成されますが、いらないので削除してルーティングを設定します。
newとcreateはresourcesを使って、confirmとcompleteはcollectionを使います。
confirmのHTTPメソッドがpostなのは、new画面のお問い合わせフォームからpostで入力データを渡されるから。

```config/routes.rb
Rails.application.routes.draw do
  get 'contacts/new'       #削除
  get 'contacts/confirm'   #削除
  get 'contacts/complete'  #削除
:
  # 追加
  resources :contacts, only: [:new, :create] do
    collection do
      post :confirm
      get :complete
    end
  end
end
```

#ビュー作成

ここまで来たらビューを作成していきます。
デザインは適宜変更してください。（Bootstrap使ってます)

まずはnew画面から
今回は、DBを保存する前に確認画面をはさみたいので、`form_withのデータの送信先をcontactsコントローラーのconfirmアクションを指定`します。

```erb:views/contacts/new.html.erb
<div class="container">
  <h2 class="my-3 ml-3">
    お問い合わせフォーム
  </h2>
  <div class="row">
    <div class="col-sm-10 mx-auto">

      <!--confirmアクションに送る-->
      <%= form_with model: @contact, local: true, url: {action: :confirm} do |f| %>
      <!--エラーメッセージ部分テンプレート化しています-->
      <%= render 'layouts/error_messages', model: f.object %>

        <div class="form-group">
          <%= f.label :name, 'お名前' %><br>
          <%= f.text_field :name, autofocus: true, placeholder:'テスト　ほげ太郎', class:'form-control' %>
        </div>
        <div class="form-group">
          <%= f.label :email, 'メールアドレス' %><br>
          <%= f.text_field :email, placeholder:'test@hoge.com', class:'form-control' %>
        </div>
        <div class="form-group">
          <%= f.label :content, 'お問い合わせ内容' %><br>
          <%= f.text_area :content, placeholder:'お問い合わせ内容は10文字以上でお願いします', class:'form-control' %>
        </div>
        <div class="d-flex justify-content-center mb-5">
          <%= f.submit '確認画面へ進む', class:'btn btn-primary' %>
        </div>
      <% end %>
    </div>
  </div>
</div>
```

次に確認画面

ここでのポイントは3つ
- form_withの送信先をcreateアクションを指定
- form_withをmethod: :postで送信
- f.hidden_fieldでデータを送信

f.hidden_fieldはデータの受け渡しに使い、通常のフォームと違ってビューに表示はされません。
確認画面からcreateアクションにnew画面で入力したデータを送りたいので、f.hidden_fieldのvalueにデータを入れてその値をcreateアクションに送ります。

ぼくもここの仕組みの理解にはかなり時間がかかりましたが、確実に理解しておきましょう。

```erb:views/contacts/confirm.html.erb
<div class="container">
  <h2 class="my-3 ml-3">
    お問い合わせ内容確認
  </h2>
  <div class="row">
    <div class="col-sm-10 mx-auto">
      <%= form_with model: @contact, local: true, url: {action: 'create'}, method: :post do |f| %>
        <div class="form-group border-bottom">
          <%= f.label :name, 'お名前' %><br>
          <!--ビューに表示させている部分-->
          <%= @contact.name %>
          <!--データの受け渡しに必要-->
          <%= f.hidden_field :name, :value => @contact.name %>
        </div>
        <div class="form-group border-bottom">
          <%= f.label :email, 'メールアドレス' %><br>
          <!--ビューに表示させている部分-->
          <%= @contact.email %>
          <!--データの受け渡しに必要-->
          <%= f.hidden_field :email, :value => @contact.email %>
        </div>
        <div class="form-group border-bottom">
          <%= f.label :content, 'お問い合わせ内容' %><br>
          <!--ビューに表示させている部分-->
          <%= @contact.content %>
          <!--データの受け渡しに必要-->
          <%= f.hidden_field :content, :value => @contact.content %>
        </div>
        <div class="d-flex justify-content-center mb-5">
          <%= f.submit '送信する', class:'btn btn-primary' %>
        </div>
      <% end %>
    </div>
  </div>
</div>
```

最後に、完了画面です。

```erb:views/contacts/complete.html.erb
<div class="container">
  <h2 class="my-3 ml-3 heading">
    お問い合わせ完了
  </h2>
  <div class="row">
    <div class="col-sm-10 mx-auto text-center mt-3">
      <h3 class="mb-5">お問い合わせありがとうございました</h3>

      <p>
        確認メールを送信させて頂きましたので、<br>
        ご確認ください。
      </p>
     
      <div class="d-flex justify-content-center mb-5">
        <%= link_to 'Topへ戻る', root_path, class:'btn btn-info mb-5 mt-3' %>
      </div>
    </div>
  </div>
</div>
```

#コントローラーの記述

ビューまで出来たので、まずはお問い合わせ機能の投稿する流れの処理をコントローラーに記述していきます。

お問い合わせ機能はログインしてなくても使える機能にしたいので、before_actionで許可しておきます。

new画面で入力したフォームの値は`Contact.new(contact_params)`で受け取ることができます。

```controllers/contacts_controller.rb
class ContactsController < ApplicationController
  before_action :authenticate_user!, except: [:new, :confirm, :complete, :create]

  def new
    @contact = Contact.new
  end

  def confirm
    @contact = Contact.new(contact_params)
  end

  def create
    @contact = Contact.new(contact_params)
    if @contact.save
      redirect_to complete_contacts_path, notice: 'お問い合わせ内容を送信しました'
    else
      render :new
    end
  end

  def complete
  end

  private

  def contact_params
    params.require(:contact).permit(:name, :email, :content)
  end
end
```

これで、お問い合わせをDBに保存するまでの処理が出来ました。
では残りのAction Mailerを使ってメール送信される機能を実装していきましょう！

#メール機能実装

まずはRailsに標準搭載されているAction Mailerを使ってmailerを作っていきます。
このmailerでメール送信の設定等を行っていきます。
コマンドを実行

```
$rails g mailer contact
```

mailers直下にcontact_mailer.rbがあるので、処理を記述していきます。
ここでは、mailの送信元、送信先、件名、bccを設定していきます。（もっと色々な設定ができるので調べてみてください）
ENV['SEND_MAIL']の部分は環境変数を使っています、わからない方も後で説明しますので安心してください。

```mailers/contact_mailer.rb
class ContactMailer < ApplicationMailer
  def contact_mail(contact)
    @contact = contact
    mail(
      from: ENV['SEND_MAIL'],  #送信元アドレス
      to: contact.email,       #送信先アドレス
      subject: 'お問い合わせを承りました',  #メールの件名
      bcc: ENV['SEND_MAIL']    #BCC送信先アドレス
    )
  end
end
```

#SMTPサーバー設定

Gmailの送信設定をします。
SMTPサーバーの設定をしていきます。（ここで使ってる環境変数も後で説明します）

```config/environments/development.rb
:
config.action_mailer.raise_delivery_errors = true  #34行目　falseをtrueに変更
:
# お問い合わせメールの送信設定
  config.action_mailer.delivery_method = :smtp
  config.action_mailer.smtp_settings = {
  address:              'smtp.gmail.com',
  port:                  587,
  domain:               'gmail.com',
  user_name:             ENV['SEND_MAIL'],
  password:              ENV['SEND_MAIL_PASSWORD'],
  authentication:       'plain',
  enable_starttls_auto:  true
}
```

#メール本文用意

実際に送られるメールを作っていきます。
htmlファイルのみ、textファイルのみしか受け取れない場合を想定して、htmlファイル、textファイルの２つ用意します。

views/contact_mailer直下に`contact_mail.html.erb`と`contact_mail.text.erb`を作成

```erb:views/contact_mailer/contact_mail.html.erb
<h5><%= @contact.name %>様　からお問い合わせを承りました</h5>
<h2>【お問い合わせ内容】</h2>
<p>
  <%= @contact.content %><br>
</p>
<p>
  お問い合わせありがとうございます。<br>
</p>

<p>
  【管理者】
</p>
```

```views/contact_mailer/contact_mail.text.erb
<%= @contact.name %>様　からお問い合わせを承りました
=====================================================

【お問い合わせ内容】

<%= @contact.content %>

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

【管理者】
```

#コントローラーに追加

contactsコントローラーのcreateされたタイミングでメールを送りたいので、コントローラーにメール送信処理を追加します。

```controllers/contacts_controller.rb
:
def create
  @contact = Contact.new(contact_params)
  if @contact.save
    ContactMailer.contact_mail(@contact).deliver  #メール送信処理追加
    redirect_to complete_contacts_path, notice: 'お問い合わせ内容を送信しました'
  else
    render :new
  end
end
:
```

#環境変数を使う

Gmailのメール設定まだの方はしておいてください。
アプリパスワードが必要になってきます。

[２段階認証の設定方法](https://enjoypclife.net/google-account-gmail-2-step-verification/)
[アプリパスワード生成方法](https://www.howtonote.jp/google-account/2step-verify/index6.html)


これでほぼ完成していますが、環境変数の設定がまだなので設定していきます。

なぜ環境変数を使うのかはセキュリティ上の問題があるからです。
今までの箇所でENVを使ってるところが環境変数なのですが、ここに直接メールアドレスやパスワードなどを入力しても動作上は全く問題ないです。

しかし、GitHubなどにあげたら全世界にメールアドレスやパスワードを公開してしまいます。
そこで、環境変数を使ってメールアドレスやパスワードを分からない状態にする必要があります。
ここまで理解した上で環境変数を使っていきましょう！

まずはgemを用意して、bundle installします。

```Gemfile
gem 'dotenv-rails'
```

```
$bundle install
```

次に`アプリケーションファイル直下`に.envファイルを作成します。
階層は.gitignoreやGemfileと同じ階層に作成してください。

.envファイルに環境変数を定義します。
ここで定義した環境変数はENV['']で呼び出すことが出来ます。

```.env
SEND_MAIL = '＜gmailのメールアドレス＞'
SEND_MAIL_PASSWORD = '＜Googleで設定した、アプリパスワード＞'
```

ただ、このままだと.envファイルもGitHubにアップロードされてしまうので、.gitignoreファイルに.envを追加してアップロードされないようにします。

必ず追記するようにしてください。
.gitignoreファイルに追加しないと結局全世界に公開されるのでなんの意味もないです。

```gitignore
:
.env
```

これでメールが送信されるので試してみてください！

#最後に

今回はお問い合わせ送信時に確認用のメールを送信する機能を実装しました。
smtpの理解ぐらいでそこまで難しくなかったと思います。

ポートフォリオサイトにてこの機能を実装して、デプロイ後の動作確認で動かなくなったときは、なんで？と思いましたが、smtpサーバーの設定を`config/environments/development.rb`でしか記述してなかったからという凡ミスでした。
`config/environments/production.rb`に同じ記述をするだけで解決しましたが、開発環境と本番環境の認識等、まだまだだなと痛感しました。。

みなさんもデプロイの際はお気をつけくださいｗｗ