[Rails]『gemを使わない管理者機能』と『ActionMailerのメール送信機能で問い合わせ機能』を実装

## 実装すること
①ユーザーは問い合わせフォームから問い合わせを送ることができる
②管理者はユーザーからのお問い合わせ一覧から問い合わせの確認ができる
③管理者が問い合わせ詳細から返信すると、ユーザーの登録しているメールアドレスに届く

## ActionMailerとは
ActionMailerを使うと、railsアプリケーションのメイラークラスやビューでメールを送信することができます。
(例)
・メールマガジンの一斉送信
・ウェブサイトに会員登録した時のthank youメール
・お問い合わせフォームの記入内容が管理者にメールでも送信される

参考：Railsガイド　https://railsguides.jp/action_mailer_basics.html

## ER図
User:Contact = 1:N の関係になります。

<img width="538" alt="問い合わせER図.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/539971/0023a58c-cc11-c4dc-88d2-9cfa8d104136.png">

## モデルの作成
今回はUserモデル、Adminモデル、Contactモデルを作成します。
Userモデル、Adminモデルはdeviseを使用してモデルを作成していきます。
deviseのviewはusersフォルダとadminsフォルダそれぞれに作成されるようにします。

### deviseの導入
```Gemfile.
gem 'devise'
```
```
$ bundle install
$ rails g devise:install
$ rails g devise User name:string
$ rails g devise Admin name:string
$ rails g devise:views users
$ rails g devise:views admins
```

## アソシエーションの確認

### Userモデル
```app/models/user.rb
class User < ApplicationRecord
 devise :database_authenticatable, :registerable,
         :recoverable, :rememberable, :validatable, :omniauthable

 has_many :contact, dependent: :destroy
end
```

### Contactモデル
reply以外のtitleとbodyにバリデーションを掛けます。

```app/models/contact.rb
class Contact < ApplicationRecord

	belongs_to :user
	#バリデーション
	validates :title, presence: true
	validates :body, presence: true
end
```

## コントローラーの作成

```
$ rails g controller users
$ rails g controller admins
$ rails g controller contacts new
```

## ルーティングの設定
・管理者側のURLは、`path_names`で推測されないように変更します。
・`namespace`で、エンドユーザー側か管理者側かが分かりやすいように、urlにusersかadminsかを記載します。


```config/routes.rb
Rails.application.routes.draw do
 devise_for :admins, 
     path: 'auth', 
     path_names: { 
     sign_in: 'xxxxxxxxxx', 
     sign_out: 'xxxxxxxxxx', 
     password: 'xxxxxxxxxx', 
     confirmation: 'xxxxxxxxxx', 
     unlock: 'xxxxxxxxxx', 
     registration: 'xxxxxxxxxx', 
     sign_up: 'xxxxxxxxxx' }, 
     controllers: {
         sessions: 'admin/sessions',
         passwords: 'admin/passwords',
         registrations: 'admin/registrations',
  }
  devise_for :users, 
     controllers: {
         sessions: 'users/sessions',
         passwords: 'users/passwords',
         registrations: 'users/registrations',
         omniauth_callbacks: 'users/omniauth_callbacks'
  }

  namespace :admins do

    resources :contacts

  end

  namespace :users do

    resources :users
    resources :contacts

  end
end
```
## メイラーの作成

```
$ rails g mailer ContactMailer
```

## サーバーの設定
今回はGmailを使用するメール送信機能を記述します。
・`raise_delivery_errors`は、メールの送信に失敗した時にエラーを出すかどうかです。
・`default_url_options`で、アプリケーションのホスト情報をメイラー内で使いたい場合は:hostパラメータを明示的に指定します。
・`delivery_method`は、デフォルトでsmtpです。
・`smtp_settings`で、詳細設定をします。
1.port => SMTPサーバーのポート番号
2.address => SMTPサーバーのホスト名
3.domain =>  HELOドメインを指定する必要がある場合はここで行なう
4.user_name => メール送信に使用するgmailのアカウント
5.password => メール送信に使用するgmailのパスワード
6.authentication => 認証方法
7.enable_starttls_auto => メールの送信にTLS認証を使用するか

```config/environments/development.rb
Rails.application.configure do
    config.action_mailer.raise_delivery_errors = true

    config.action_mailer.default_url_options = { :host => 'localhost:3000' }
    config.action_mailer.delivery_method = :smtp

    config.action_mailer.smtp_settings = {
        port:                 587,
        address:              'smtp.gmail.com',
        domain:               'gmail.com',
        user_name:            '<YOUR EMAIL ADDRESS>',
        password:             '<YOUR EMAIL PASSWORD>',
        authentication:       'login',
        enable_starttls_auto: true
    }
end

```

## メイラーを編集
`application_mailer`には、全メイラー共通の設定を、
`sample_mailer`には、メイラー個別の設定をします。

### application_mailer.rb
`defaultメソッド`で、共通の処理・設定を記述します。

```app/mailers/application_mailer.rb
class ApplicationMailer < ActionMailer::Base
  default from: 'from@example.com'
  layout 'mailer'
end
```

### contact_mailer.rb
管理者の返信が、ユーザーのemailに届くようにします。
個別の設定にはmailメソッドを使用します。
・`send_when_replyedメソッド`を呼び出す時に渡されるユーザーの情報から、
emailアドレスだけを取り出してメールの送信先としてします。
・`mailメソッド`が呼び出されると、メール本文が記載されているビューが読み込まれます。
インスタンス変数でメイラービューに値を渡してあげたいので、インスタンス変数を用意しています。

```app/mailers/contact_mailer.rb
class ContactMailer < ApplicationMailer
	default from: 'from@example.com'

	def send_when_admin_reply(user, contact) #メソッドに対して引数を設定
    @user = user #ユーザー情報
    @answer = contact.reply #返信内容
    mail to: user.email, subject: '【サイト名】 お問い合わせありがとうございます'
  end
end
```

## メールの本文を作成する
１つはHTMLフォーマット、もう一つはテキストメールです。
顧客によってはHTMLフォーマットのメールを受け取ることができない / 受け取りたくない人もいるので、テキストメールも作成しておくのが最善です。

### contact_mailer/send_when_admin_reply.html.erb

```app/views/contact_mailer/send_when_admin_reply.html
<h2><%= @user.name %> 様</h2>
　<p>この度は、お問い合わせありがとうございました。<br>
　以下でご質問の回答となっておりますでしょうか。</p>

  <p><%= @answer %></p>

  <p>今後とも弊社をよろしくお願いいたします。</p>
```

### contact_mailer/send_when_admin_reply.text.erb

```app/views/contact_mailer/send_when_admin_replyd.text

  <%= @user.name %> 様

  この度は、お問い合わせありがとうございました。
　以下でご質問の回答となっておりますでしょうか。

  <%= @answer %>

  今後とも弊社をよろしくお願いいたします。
```


## 問い合わせフォームの作成(ユーザー側)
### users/contacts_controller.rb

```app/controllers/users/contacts_controller.rb
class Users::ContactsController < ApplicationController

	def new
		@contact = Contact.new
	end

	def create
		@contact = Contact.new(contact_params)
		@contact.user_id = current_user.id
		if @contact.save
			redirect_to root_path
		else
			@contacts = Contact.all
			@users = User.all
			render :new
		end
	    flash[:success] = 'お問い合わせを送信しました。'
	end

	private
	def contact_params
		params.require(:contact).permit(:title, :body, :reply)
	end
end
```

### users/contacts/new.html.erb

```app/views/users/contacts/new.html
  <% if @contact.errors.any? %>
        <%= @contact.errors.count %>件のエラー。
        <% @contact.errors.full_messages.each do |message| %>
          <%= message %>。
        <% end %>
  <% end %>
  <h2 class="contact-title">お問い合わせフォーム</h2>
      <%= form_for @contact, url: users_contacts_path do |f| %>
          <%= f.label :タイトル %>
              <%= f.text_field :title, class:"form-control" %>
          <%= f.label :内容 %>
              <%= f.text_field :body, class:"form-control" %>
          <%= f.submit "送信" %>
      <% end %>

```

## 返信処理の作成(管理者側)
・コントローラーのアクションによって、`application_mailer.rb / contact_mailer.rb`を起動します。
・**流れは、
①管理者が問い合わせ一覧(index)から
②問い合わせ詳細(edit)へ飛び、
③返信を送信(update)し、
④その内容をメールでユーザーに送信します。**
・ユーザーが問い合わせをした時点で、Contactsテーブルの中に、
レコードが作成されます。その中に「返信(reply)」というカラムが用意されています。
updateアクションを使って、すでにあるレコードの中に管理者の返信のデータだけを更新することで、ContactMailerのsend_when_admin_replyアクションが発火します。
>`deliver_now`について
>>いくつかの参考書や解説サイトでは、deliver_nowメソッドではなくdeliverメソッドを使うように記載されています。
じつは、deliver_nowを使ってメール送信すると、メール送信が完了するまでRailsが反応しなくなることがあります。そのため、Rails4.2から「Active Job」という非同期処理が導入されました。「Active Job」を使うと、メール送信を裏で行わせながら処理を行うことができますので、メール送信完了を待つ必要がなくなるのです。
そのため、「すぐに送信する」ということが分かるように、deliverではなく、deliver_nowという名称になっているのです。
参考：https://web-camp.io/magazine/archives/19143

### admins/contacts_controller.rb

```app/controllers/admins/contacts_controller.rb
class Admins::ContactsController < ApplicationController

	def index
		@contacts = Contact.page(params[:page]).order(created_at: :desc).per(16)
		@users = User.all
	end

	def edit
		@contact = Contact.find(params[:id])
	end

	def update
		contact = Contact.find(params[:id]) #contact_mailer.rbの引数を指定
	   	contact.update(contact_params)
	   	user = contact.user
	   	ContactMailer.send_when_admin_reply(user, contact).deliver_now #確認メールを送信
	   	redirect_to admins_items_path
	end

	def destroy
		contact = Contact.find(params[:id])
		contact.destroy
		@contacts = Contact.page(params[:page]).order(created_at: :desc).per(16)
		@users = User.all
		render :index
	end

	private
	def contact_params
		params.require(:contact).permit(:title, :body, :reply)
	end
end

```

### 問い合わせ一覧(admins/contacts/index.html.erb)

```app/views/admins/contacts/index.html
<h2>問い合わせ一覧</h2>
    <table>
        <thead>
	    <tr>
	        <th>ユーザー名</th>
		<th>問い合わせタイトル</th>
		<th>受信日</th>
		<th></th>
		<th></th>
	    </tr>
	</thead>
	<tbody>
	    <% @users.each do |user| %>
	        <% user.contact.each do |contact| %>
		      <tr>
		          <td><%= contact.user.name %><%= user.name %></td>
			      <td><%= contact.title %></td>
			      <td><%= contact.created_at.strftime("%Y年%m月%d日 ") %></td>
			      <td><%= link_to "返信", edit_admins_contact_path(contact) %></td>
			      <td><%= link_to "削除", admins_contact_path(contact), method: :delete %></td>
		       </tr>
		    <% end %>
	   <% end %>
        </tbody>
</table>
```

### 問い合わせ内容(admins/contacts/edit.html.erb)

```app/views/admins/contacts/edit.html
<h2>お問い合わせ内容</h2>
    <%= form_for @contact, url: admins_contact_path do |f| %>
        <h3>顧客名</h3>
	    <%= @contact.user.name %>さん
	    <h3>問い合わせ日</h3>
	    <%= @contact.created_at.strftime("%Y年%m月%d日 ") %>
	    <h3>タイトル</h3>
	    <%= @contact.title %>
	    <h3>問い合わせ内容</h3>
	    <%= @contact.body %>
	    <h3>返信</h3>
	    <%= f.text_area :reply, class:"form-control", placeholder: "返信内容" %>
	    <%= f.submit "送信" %>
    <% end %>
```

## Google側での設定
・2段階認証をオンにする
https://support.google.com/accounts/answer/185839?co=GENIE.Platform%3DDesktop&hl=ja
・アプリケーション用のパスワードを発行する
https://support.google.com/accounts/answer/185833?hl=ja
・2段階認証プロセスで使用するパスワードとコード
https://support.google.com/accounts/answer/1070457?hl=ja


## 最後に
最後までご覧いただきありがとうございます。
初学者ですので間違っていたり、分かりづらい部分もあるかと思います。
何かお気付きの点がございましたら、お気軽にコメントいただけると幸いです。

## 参考
Railsガイド
https://railsguides.jp/action_mailer_basics.html
Action Mailer でメール送信機能をつくる
https://qiita.com/annaaida/items/81d8a3f1b7ae3b52dc2b
Gmail 経由での SMTP メール送信が出来ない時の解決法 (ユーザー名とパスワードは合っているのに。。)
https://qiita.com/Yinaura/items/6886682a607951a71bac