[Rails]いいねしてくれたユーザに、新規投稿をメール通知する（開発環境編）

## きっかけ
#### 経緯
就活中の選考での課題として
[投稿者のレビューにいいねをしたユーザに、その投稿者が新規でレビューしたときにメールでお知らせする機能]
の作成が出てきましたので、アウトプット用に記載いたします。

#### 開発環境
Ruby 3.1.2
Rails 6.1.7.3

## 手順
この機能を実装するためには、以下の手順で行うことにします。

#### ①モデルの設定
ユーザとレビュー、いいねのモデルを設定します。ユーザとレビュの関連付け、いいねとユーザ・レビューとの関連付けをします。
~~~app/models/user.rb
class User < ApplicationRecord
  has_many :contents
  has_many :favorites
end
~~~
~~~app/models/content.rb
class Content < ApplicationRecord
  belongs_to :user
  has_many :favorites
end
~~~
~~~app/models/favorite.rb
class Favorite < ApplicationRecord
  belongs_to :user
  belongs_to :content
end
~~~
#### ②投稿者が新規でレビューを作成した時の処理
レビューが新たに作成されるたびに、その投稿者の過去レビューにいいねしたユーザのリストを取得します。
~~~app/models/content.rb
class Content < ApplicationRecord
    #　以上に諸々の処理を記載

    # 新しいレビューがデータベースに保存された後に以下のメソッドが実行する。（コールバック）
    after_create :send_message_to_favorited_users
    
    private
    
    # いいねしたユーザのIDリストを取得するメソッド
    def send_message_to_favorited_users
      favorited_users = Favorite.where(content_id: self.user.contents.pluck(:id)).pluck(:user_id).uniq
      favorited_users.each do |user_id|
        user = User.find(user_id)
        # 投稿した本人にメールを送信しない
        unless user_id == self.user.id
          FavoritedUserMailer.new_content_message_email(user, self).deliver_now
        end
      end
    end
end
~~~

##### コールバックで処理
まず、`after_create`はActive Record モデル内で使用されるコールバックの1つです。レビューが新規作成された後に `send_message_to_favorited_users メソッド`が自動的に呼び出されるようにするためのものです。

##### 過去にいいねしたユーザをリスト化する処理
次に、以下のコードの処理について
~~~content.rb
favorited_users = Favorite.where(content_id: self.user.contents.pluck(:id)).pluck(:user_id)`
~~~
`Favoriteモデル`から、指定された条件に合致するレコードを取得しています。
今回の指定した条件は、`content_id`カラムでレビューしたユーザが投稿したレビューのIDリストに含まれているものです。つまり、レビューを「いいね」したレコードを取得しています。
さらに、`.pluck(:user_id)`で上記のクエリ結果（Favorite レコード）から、user_id カラムの値を取得しています。これにより、レビューを「いいね」したユーザーのIDリストを取得することができます。
最後に、`favorited_users = `で上記の手順で取得した「いいね」したユーザリストを、`favorited_users変数`に代入しています。
つまり、上記コードは簡単に言うと投稿者が作成したレビューの中で「いいね」されたレコードを抽出するためのものです。

###### リスト化したユーザにメール送信する処理
~~~ruby
user = User.find(user_id)
~~~
先ほどの、`favorited_users変数`から「いいね」したユーザIDを使用して、対応するユーザをデータベースから取得します。
~~~ruby
unless user_id == self.user.id
   FavoritedUserMailer.new_content_message_email(user, self).deliver_now
end
~~~
そして、投稿者を除外する`unless文`を用いて、投稿者以外にメールを送信します。

#### ③通知メールの送信
取得した`いいねユーザリスト`に対して、通知メールを送信する処理を行います。Railsでは`Action Mailer`を使用してメールを送信できますので、メール文を決め、Action Mailerを設定します。

##### Action Mailerを生成する
まず、メール送信するために必要なApplicationMailerを継承するクラスは、generateコマンドから作成します。ターミナルで以下のコマンドを行います。
~~~
$ rails generate mailer FavoritedUserMailer
~~~
以上で、以下のフォルダ、ファイルが作成されました。
~~~
Running via Spring preloader in process 616
      create  app/mailers/favorited_user.rb
      create  app/mailers/application_mailer.rb
      invoke  erb
      create    app/views/favorited_user
      invoke  test_unit
      create    test/mailers/favorited_user_test.rb
      create    test/mailers/previews/favorited_user_preview.rb
~~~
##### メールの宛先や件名を指定する
~~~app/mailers/favorited_user_mailer.rb
class FavoritedUserMailer < ApplicationMailer
  def new_content_message_email(user, content)
    @user = user
    @content = content
    mail(to: @user.email, subject: 'いいねしたユーザが新しいレビューを投稿しました！')
  end
end
~~~
　今回、いいねしたユーザに送るメールということで`FavoritedUserMailerクラス`で`new_content_message_emailメソッド`を作成します。
`to(メールの送信先アドレス)`を指定したり、`subject`で件名を指定することできます。
※Railsのコントローラと似た形式でコーディングを行います。

さらに、下記のように`default from:...`で差出人のメールアドレスを指定できます。
~~~app/mailers/application_mailer.rb
class ApplicationMailer < ActionMailer::Base
  default from: 'totono-ru@saunar.com'
  layout 'mailer'
end
~~~

##### ビューの生成（メール内容の作成）
`mailer`で定義した変数はビューで使えます。今回は`app/views/favorited_user_mailer`に例えば以下のようなメッセージを作成することができます。
~~~
<!DOCTYPE html>
<html>
  <head>
    <meta content="text/html; charset=UTF-8" http-equiv="Content-Type" />
  </head>
  <body>
    <h1> <%= @user.name %> 様</h1>
    <h3>いつもご利用いただきありがとうございます。</h3>
    <p><%= @content.user.name %> さんが新しいレビューを投稿しました！</p>
    <p>詳細な内容を見るには以下のリンクをクリックしてください。</p>
    <p>レビュータイトル: <%= @content.title %></p>
    <a href="<%= content_url(@content) %>">新しいレビューを見る</a>
    <br>
    <p>以上です。</p>
    <p>今後ともご愛顧いただきますよう、よろしくお願いいたします。</p>
  </body>
</html>
~~~
###### ビューでURLを生成する方法
~~~
<a href="<%= content_url(@content) %>">新しいレビューを見る</a>
~~~
>リンク先としてURLを記載するには、以下の記事を参考にして設定変更を行いました。

https://qiita.com/Atsushi_/items/ec56b51f7057bd399ae8

##### メール送信のトリガーの確認
~~~app/models/content.rb
    FavoritedUserMailer.new_content_message_email(user, self).deliver_now
~~~
今回は先ほど作成した`contentモデル`で作成した以上のコーディングをトリガーにして送信を行います。

#### ④letter_opener_webでメールをブラウザでプレビューする
開発環境では、テストなどでメールを送信する代わりにデフォルトのブラウザでプレビューする方が楽です。それでは、以下に手順を記載します。
##### 導入手順
まずhはGem導入から
~~~Gemfile.rb
group :development do
  gem 'letter_opener_web', '~> 1.0'
end
~~~
次に、`routes.rb`にletter_opener用のルーティングを追加します。
~~~ config/routes.rb
Rails.application.routes.draw do
  # 下記を追加
  mount LetterOpenerWeb::Engine, at: '/letter_opener' if Rails.env.development?
  # 既存のルーティングが以下に続く
end
~~~
そして、
~~~ config/environments/development.rb
require 'active_support/core_ext/integer/time'

Rails.application.configure do
  ...

  # 下記を追加
  config.action_mailer.delivery_method = :letter_opener_web
end
~~~
以上で、`letter_opener_web`の導入は完了しました。

##### 送信メールの内容を確認
メール送受信を確認するには、`http://localhost:3000/letter_opener/`のようにrootに`letter_opener/`追加してアクセスすると下記のような画面が表示されていれば設定は完了しています。
![LetterOpenerWeb.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3338070/c800394c-2e59-7c5a-e83a-3376f6e21f05.jpeg)

## 終わりに
以上で、いいねしたユーザのリスト化からメール送信、開発環境でのメールブラウザ確認までを行いました。
メール設定など基本を改めて勉強できてよかったです。誤りがありましたらコメントいただけると幸いです！
最後まで閲覧いただき、ありがとうございました。

## 参考にした記事
以下の記事を参考に作成させていただきました。ありがとうございます！

https://qiita.com/sazumy/items/e0e9f1aaeed47c616c6b

https://www.sejuku.net/blog/48739

https://railsguides.jp/action_mailer_basics.html

https://karlley.hatenablog.jp/entry/2022/10/01/130859

https://qiita.com/sazumy/items/c6193c4c2c352ac152ec

https://qiita.com/k-o-u/items/31e4a2f9f5d2a3c7867f

https://qiita.com/mokio/items/1690aa60260c9da1f7b9