【Rails6】ActionMailerを用いたお問い合わせ機能の実装

#はじめに

転職活動用ポートフォリオ作成中です。今回、ユーザーからのお問い合わせ機能を実装したため、備忘録及び復習のため記述します。

##環境

Ruby on Rails'6.0.0'
Ruby'2.6.5'

##①ルーティングの記述

```ruby:config/routes.rb
resource :contacts, only: [:new, :create] do
 get "/thanks" => "contacts#thanks"
end
```

今回は、ユーザーがお問い合わせを送信後、「お問い合わせいただきありがとうございました！」のようなページを表示させる流れです。

##②モデルの記述

rails g model contactコマンドでモデルを作成後、マイグレーションファイルの記述などを行っていきます。

```ruby:db/migrate/20201204073627_create_contacts.rb
class CreateContacts < ActiveRecord::Migration[6.0]
  def change
    create_table :contacts do |t|
      t.string :name, null: false
      t.string :email, null: false
      t.text :content, null: false
      t.timestamps
    end
  end
end
```

今回は、名前・返信用のメールアドレス・お問い合わせ内容を指定しました。その後、rails db:migrateコマンドを実行します。

```ruby:app/models/contact.rb
class Contact < ApplicationRecord
  validates :name, :email, :content, presence: true
end
```

名前・メールアドレス・お問い合わせ内容が空では保存できないようにバリデーションを設定します。

##③ActionMailerの設定

今回はユーザーからお問い合わせが送信されると、内容が管理者へメールで届くような機能にしたいと思います。そこでActionMailerを使用しました。
まず、rails g mailer ContactMailerコマンドを実行します。すると以下のファイルが生成されるためお問い合わせが来たら管理者へメールを送信する記述をしていきます。

```ruby:app/mailers/contact_mailer.rb
class ContactMailer < ApplicationMailer
  def contact_mail(contact)
    @contact = contact
    mail to: '（管理者のメールアドレス）@gmail.com', subject: '（メールのタイトル）'
  end
end
```

続いては新たにviewファイルが生成されているため、メールの本文を記述していきます。

```ruby:app/views/contact_mailer/contact_mail.html.erb
<p>ユーザーネーム:<%= @contact.name %></p>
<p>メールアドレス:<%= @contact.email %></p>
<p>お問い合わせ内容:<%= @contact.content %></p>
```

##④コントローラーの記述

それではコントローラーの記述をしていきます。

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

  def create
    @contact = Contact.new(contact_params)
    if @contact.save
      ContactMailer.contact_mail(@contact).deliver
      redirect_to thanks_contacts_path
    else
      render :new
    end
  end

  def thanks
  end

  private

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

これにより、createアクションが呼び出され、お問い合わせが正常に保存されると、メール送信処理も走り出すように設定できました。

##⑤viewの記述

お問い合わせのviewファイルの記述をしていきます。

```ruby:app/views/contacts/new.html.erb
<div class="container">
  <div class="row">
    <div class="offset-sm-2 col-sm-8 offset-sm-2">
    <%= form_with model: @contact, local: true do |f| %>
      <h5 class='form-header-text text-center'><i class="far fa-paper-plane fa-2x my-orange"></i>　お問い合わせ</h5>

  <%= render 'layouts/error_messages', model: f.object %>
      <div class="form-group">
        <div class='form-text-wrap'>
          <label class="form-text" for="name">お名前</label>
          <span class="badge badge-danger">必須</span>
        </div>
        <div class='input-name-wrap'>
          <%= f.text_field :name, class:"input-name", id:"name", placeholder:"例) 田中太郎" %>
        </div>
      </div>

      <div class="form-group">
        <div class='form-text-wrap'>
          <label class="form-text" for="email">メールアドレス</label>
          <span class="badge badge-danger">必須</span>
        </div>
        <%= f.email_field :email, class:"input-default", id:"email", placeholder:"PC・携帯どちらでも可", autofocus: true %>
      </div>

      <div class="form-group">
        <div class='form-text-wrap'>
          <label class="form-text" for="content">内容</label>
          <span class="badge badge-danger">必須</span>
        </div>
        <%= f.text_area :content, class:"article-input-default", id:"content", autofocus: true %>
      </div>
 
      <div class='contact-btn text-center'>
        <%= f.submit "送信する" ,class:"btn btn-outline-danger w-50" %>
      </div>
    <% end %>
    </div>
  </div>
</div>
```

```ruby:app/views/contacts/thanks.html.erb
<div class="container">
  <div class="row">
    <div class="offset-sm-2 col-sm-8 offset-sm-2">
      <h5 class='header-text text-center'><i class="far fa-smile fa-lg my-orange"></i>　お問い合わせありがとうございました</h5>
      <%= link_to 'トップページへ戻る', root_path %>
    </div>
  </div>
</div>
```

##⑥Gmailの設定

最後に今回アドレスで使用するGmailの設定を行いました。

```ruby:config/environments/development.rb
(中略)
  config.action_mailer.perform_deliveries = true
  config.action_mailer.raise_delivery_errors = true
  config.action_mailer.delivery_method = :smtp
  config.action_mailer.smtp_settings = {
    address: 'smtp.gmail.com',
    domain: 'gmail.com',
    port: 587,
    user_name: '(管理者のメールアドレス）@gmail.com',
    password: ENV["GMAIL_KEY"],
    authentication: 'plain',
    enable_starttls_auto: true
  }
(中略)
```

すでに他の設定も記述されているかと思うので、間に記述しました。
GmailのパスワードはGitHub上に上がってしまっては、大変なことになってしまうので、環境変数を設定します。設定する方法は様々あると思いますが、私はvimコマンドを使用して「.zshrc」ファイルに設定しました。

```ruby:ターミナル
% vim ~/.zshrc
```

上記のコマンドでファイルを開き、insertモードにしてから記述していきます。

```ruby:.zshrcファイル
（中略）
export GMAIL_KEY = "Gmailのパスワード" 
（中略）
```

こちらで設定が完了しました！

##⑦テストコードの実装

おまけとしてRspecを使用してテストコードも実装しました。

#####FactoryBot

```ruby:spec/factories/contacts.rb
FactoryBot.define do
  factory :contact do
    name { Faker::Name.name }
    email { Faker::Internet.email }
    content { Faker::Lorem.sentence }
  end
end
```

#####単体テストコード

```ruby:spec/models/contact_spec.rb
require 'rails_helper'

RSpec.describe Contact, type: :model do
  before do
    @contact = FactoryBot.build(:contact)
  end

  describe 'お問い合わせの送信' do
    context 'お問い合わせが送信できる場合' do
      it '全ての要素が存在すれば投稿できる' do
        expect(@contact).to be_valid
      end
    end
    context 'お問い合わせが送信できない場合' do
      it 'nameが空では送信できない' do
        @contact.name = nil
        @contact.valid?
        expect(@contact.errors.full_messages).to include('お名前を入力してください')
      end
      it 'emailが空では送信できない' do
        @contact.email = nil
        @contact.valid?
        expect(@contact.errors.full_messages).to include('メールアドレスを入力してください')
      end
      it 'contentが空では送信できない' do
        @contact.content = nil
        @contact.valid?
        expect(@contact.errors.full_messages).to include('お問い合わせ内容を入力してください')
      end
    end
  end
end
```

#####結合テストコード

```ruby:spec/system/contacts_spec.rb
require 'rails_helper'

RSpec.describe 'お問い合わせ送信', type: :system do
  before do
    @contact = FactoryBot.build(:contact)
  end

  context 'お問い合わせの送信ができるとき' do
    it '正しい情報を入力すれば、お問い合わせを送信できる' do
      visit root_path
      expect(page).to have_content('お問い合わせ')
      visit new_contacts_path
      fill_in 'お名前', with: @contact.name
      fill_in 'メールアドレス', with: @contact.email
      fill_in '内容', with: @contact.content
      expect do
        find('input[name="commit"]').click
      end.to change { Contact.count }.by(1)
      expect(current_path).to eq thanks_contacts_path
      click_link 'トップページへ戻る'
      expect(current_path).to eq root_path
    end
  end

  context 'お問い合わせの送信ができないとき' do
    it '正しい情報を入力しなければ、お問い合わせは送信できない' do
      visit root_path
      expect(page).to have_content('お問い合わせ')
      visit new_contacts_path
      fill_in 'お名前', with: ''
      fill_in 'メールアドレス', with: ''
      fill_in '内容', with: ''
      expect do
        find('input[name="commit"]').click
      end.to change { Contact.count }.by(0)
      expect(current_path).to eq contacts_path
    end
  end
end
```

#終わりに
https://qiita.com/mmdrdr/items/9c5dd4ca886f034fb0ef
https://qiita.com/hirotakasasaki/items/ec2ca5c611ed69b5e85e

上記の記事を参考にさせていただきました。ありがとうございました。

メール送信のテストもしてみたいと思うので、調べてみます！！
誤っている点ありましたらご指摘ください。