【Rails】Herokuでのお問い合わせフォームの作成方法

## お問い合わせフォームの実装

利用環境
・ruby 2.7.3
・Rails 6.1.4

## 前提
お問い合わせフォームでメールを受信するためのメールアドレスを作成
ここではGmailを登録する前提で進めます。

## Gmailアカウントの設定
・２段階認証に設定する
・アプリで利用するためのパスワードを作成
　リンク先：https://myaccount.google.com/u/3/security


## ActionMailerの設定
本番環境のみでの設定とします。
開発環境でのメール確認に興味がある方はこちらを参考にしてみてください。
https://github.com/fgrehm/letter_opener_web

```terminal:ターミナル
touch config/initializers/mail.rb
```

```ruby:config/initializers/mail.rb
if Rails.env.production?
  # 自身のドメインに置き換えてください
  host = "Herokuのサブドメイン.herokuapp.com"
  # メール配信に失敗した場合にエラーを発生
  ActionMailer::Base.raise_delivery_errors = true
  ActionMailer::Base.delivery_method = :smtp
  ActionMailer::Base.default_url_options = { host: host }
  ActionMailer::Base.smtp_settings = {
    port: 587,
    address: "smtp.gmail.com",
    user_name: Rails.application.credentials.gmail[:address],
    password: Rails.application.credentials.gmail[:password],
    domain: host,
    authentication: "plain"
  }
end
```

## サンプルメール
先ほど作成したGmailアドレスをここへ入力します。

```ruby:app/mailers/application_mailer.rb
class ApplicationMailer < ActionMailer::Base
  # 送信元として表示するメールアドレスと表示名
  default from: 'アプリ名 <sample@gmail.com>'
  layout 'mailer'
end
```

お問い合わせ用のメイラーを作成します。

```terminal:ターミナル
rails g mailer ContactMailer user_email admin_email
```

上記のコマンド実行によって、HTML形式とtext形式が作成されます。
今回はtext形式のビューのみ使用するのでHTML形式のビューは削除します。

```terminal:ターミナル
rm -f app/views/contact_mailer/user_email.html.erb app/views/contact_mailer/admin_email.html.erb
```



## お問い合わせフォームの実装
ユーザーがお問い合わせを送信したタイミングでメールを送信できるようにします。

お問い合わせ内容をデータベースに保存できるようモデル・テーブルの作成
迷惑メール対策のためにIPアドレスを保存するカラムも作成

```terminal:ターミナル
rails g model Contact name:string email:string content:text remote_ip:inet
rails db:migrate
```

モデルにバリデーションを追加

```ruby:app/models/contact.rb
class Contact < ApplicationRecord
  VALID_EMAIL_REGEX = /\A[\w+\-.]+@[a-z\d\-.]+\.[a-z]+\z/i

  validates :name, length: { maximum: 48 }
  validates :email, presence: true, format: { with: VALID_EMAIL_REGEX }, length: { maximum: 256 }
  validates :content, presence: true, length: { maximum: 2000 }
  validates :remote_ip, presence: true
end
```

ユーザーと管理者に送信するよう実装

```ruby:app/mailers/contact_mailer.rb
class ContactMailer < ApplicationMailer
  ADMIN_EMAIL = "自分のアドレス"

  def user_email(contact)
    @contact = contact
    @name = contact.name.presence || contact.email
    subject = "【アプリ名】お問い合わせを受付いたしました"

    mail(to: contact.email, subject: subject)
  end

  def admin_email(contact)
    @contact = contact
    @name = contact.name.presence || contact.email
    subject = "【アプリ名】お問い合わせがありました"

    mail(to: ADMIN_EMAIL, subject: subject)
  end
end
```

ユーザーに送信されるメール内容を入力します。

```rails:app/views/contact_mailer/user_email.text.erb
<%= @name %> 様

この度は，「アプリ名」にお問い合わせいただき，誠にありがとうございます。
回答の準備が出来次第，担当者よりご連絡いたします。

今後とも「アプリ名」をよろしくお願いいたします。

=*=*=*=*=*=*=*= お問い合わせ内容 =*=*=*=*=*=*=*=

<%= @contact.content %>

=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=

当メールの送信アドレスは配信専用となっております。
ご連絡は「お問い合わせフォーム」よりお願いいたします。 
```

管理者に届くメール内容

```rails:app/views/contact_mailer/admin_email.text.erb
<%= @name %> 様より，「アプリ名」にお問い合わせがありました。

=*=*=*=*=*=*=*= お問い合わせ内容 =*=*=*=*=*=*=*=

【氏名】<%= @contact.name %>
【メールアドレス】<%= @contact.email %>
<%= @contact.content %>

=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*= 
```

## お問い合わせページの作成

不要なファイルがあれば削除してください。

```terminal:ターミナル
rails g controller contacts index create --skip-assets --skip-helper --skip-routes
touch config/locales/ja.yml app/views/contacts/_error_messages.html.erb app/views/contacts/_submit_fields.html.erb
```

ルートに下記を追加

```ruby:config/routes.rb
resources :contacts, only: %i[index create]
```

```ruby:app/controllers/contacts_controller.rb
class ContactsController < ApplicationController
  def index
    @contact = Contact.new
  end

  def create
    @contact = Contact.new(contact_params)
    if @contact.save
      # ユーザーにメールを送信
      ContactMailer.user_email(@contact).deliver_now
      # 管理者にメールを送信
      ContactMailer.admin_email(@contact).deliver_now
      redirect_to root_path
    else
      render :index
    end
  end

  private

  # IPアドレスをパラメータに追加
  def contact_params
    params.require(:contact)
          .permit(:name, :email, :content)
          .merge(remote_ip: request.remote_ip)
  end
end
```

ビューはとりあえず最低限表示される程度に実装しています。

```rails:app/views/contacts/index.html.erb
<h1>お問い合わせ</h1>

<%= form_with model: @contact, local: true do |form| %>
  <%= render "error_messages", form: form %>

  <%= render "submit_fields", form: form %>
  <%= form.submit "送信" %>
<% end %>
```

```rails:app/views/contacts/_error_messages.html.erb
<% if @contact.errors.any? %>
  <div id="error_explanation">
    <h2><%= @contact.errors.count %> つのエラーがあります</h2>

    <ul>
      <% @contact.errors.full_messages.each do |message| %>
        <li><%= message %></li>
      <% end %>
    </ul>
  </div>
<% end %>
```

```rails:app/views/contacts/_submit_fields.html.erb
<div>
  <%= form.label :name %> (任意)
  <%= form.text_field :name %>
</div>
<div>
  <%= form.label :email %> (必須)
  <%= form.email_field :email, required: true, pattern: "^[a-z0-9._%+-]+@[a-z0-9.-]+\\.[a-z]+$" %>
</div>
<div>
  <%= form.label :content %> (必須)
  <%= form.text_area :content, required: true %>
</div>
```

## 最後に
とりあえず最低限こんな感じで動きました。
お問い合わせを送信する前の確認画面などを実装するといいかもしれませんね。
いやー長かった・・・。
もしおかしな点がありましたらご指摘お願いします。