Rails6で通知機能を実装（ポリモーフィック）手直し版

前回投稿した通知機能をポリモーフィックを使って実装しましたが、途中からうまく使えていませんでした。
下記記事のenumの設定から分岐してviewまでを修正して書いていきます。
この記事から見られた場合はenumまでは下記記事を見てください

https://qiita.com/ttymryo/items/79dcf9fd83d0c09e8775

## enumの設定

```ruby:model/activity.rb
  enum action_type: {
    favorited_the_diary:      0,
    followed_you:             1,
    commented_on_the_diary:   2
  }
```

通知する内容をなんとなく翻訳にかけて分かるように書いてあります
後で日本語化するので単語一個だと分かりにくくなります

## create時に通知を入れる
通知したい内容がcreateされたときにactivityをcreateするようにします

```ruby:model / favorite.rb comment.rb relationship.rb
after_create_commit :create_activities

private

def create_activities
  Activity.create!(subject: self, user_id: ユーザーのID, action_type: Activity.action_types[:enumで設定した内容])
end
```

各モデルにafter_create_commitを入れます
これはDB保存が完了した時をトリガーに動きます
詳しくは[Railsガイド](https://railsguides.jp/active_record_callbacks.html)をみてください

今回入れたい通知は保存したタイミングで大丈夫ですが内容によって好きなタイミングでcreate_activityが動くように書いてください
<br>
create_activitiesの説明
* subject: self
これでDB保存ができた内容がはいります(typeとid)

* user_id: ユーザーのID
DB保存した内容からuser_idを取り出して書いてください
例：いいねのとき
user_id: diary.user.id(いいねに紐づいてる投稿に紐づいてるユーザーのID)

* action_type: Acticity.action_types[:enumで設定した内容]
右で書いた文はenumで設定した内容から値を出力しています
例：いいねのとき
action_type: Activity.action_types[:favorited_the_diary]これでaction_typeに0が入ります

ターミナルで確認してみます
いいねが保存された後にcreate_activitiesが動いて保存されたのが確認できます
ここで正確に保存が出来ているか確認してください
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2821961/a2cd7ed4-8edb-2556-a512-ac8a61e77dc0.png)
※user_idがいいねとアクティビティで同じ値なのは自分の投稿をいいねしたためです

## コントローラーとルーティングの設定
ここからは好きなように書いて問題ありません
未読だけ表示、同じ投稿へのいいねはまとめる、どのタイミングで見たことにするのか、いろいろカスタマイズしてください

ここではすべての通知の一覧表示と一覧表示から各通知を押したときに既読にする設定で実装しています
viewファイルのすべては書きません、レイアウトは頑張ってください

rails g でコントローラーを作って設定していきます

```ruby:activities_controller.rb
class Public::ActivitiesController < ApplicationController
  before_action :authenticate_user

  def index
    @activities = current_user.activities.order(created_at: :desc).page(params[:page]).per(20)
  end

  def read
    activity = current_user.activities.find(params[:id])
    unless activity.read?
      activity.update(read: true)
    end
    redirect_to activity.subject.redirect_path
  end
```

コントローラーの説明

* before_action :authenticate_user
deviseのメソッドでユーザー以外をはじきます

* index
表示したい内容をkaminariを使って20件ずつ作成日時の新しい順でいれます

* read
通知一覧から各通知をクリックした際にすべてここを通して処理します
通知を読んだらtrueにして各通知元へリダイレクトさせます

* redirect_to activity.subject.redirect_path
ここでsubjectにモデルの情報が入っているので、モデルにあるインスタンスメソッドを呼び出します
ポリモーフィック設定でcaseやifを使わずに分岐させることが出来ます

モデルにredirect_pathを書いていきます

```ruby:model/favorite.rb comment.rb relationship.rb
  def redirect_path
    #コメントの場合
    "/#{diary.user.name_id}/diaries/#{diary.id}"
  end
```
Rrefixで書けないためURLで書きます
#{}でかこうことで変数展開ができます
好きなリダイレクト先を入れてください。

<br />


```ruby:routes.rb
  resources :activities, only: [:index] do
    patch :read, on: :member
  end
```
resurcesに読んだ時の処理をするルートをネストで入れてます



## viewの作成
最低限の表示だけ書いています

```erb:activities/index.html.erb
<% if @activities.present? %>
  <% @activities.each do |activity| %>
    <%= link_to read_user_activity_path(current_user.name_id,activity), method: :patch do %>
      <%= @activity.subject.name %>さんが<%= @activity.action_type_i18n %>
    <% end %>
  <%= paginate @activities %>
  <% end %>
<% else %>
  <p>通知はありません</p>
<% end %>

```
ここでもポリモーフィックの利点である同じコードでモデルごとに違う内容を表示します。
subject.nameと書きましたがnameカラムがないモデルもあります
モデルにインスタンスメソッドを書きます

```ruby:model/comment.rb favorite.rb relationship.rb
  def name
    #favorite.rbの場合
    user.name
  end
```


## enumの日本語化

activityのaction_typeを日本語化して表示させます
詳しくはrails日本語化で検索するとたくさん出てきます
下記方法でできなかったら調べてみてください

application.rbに日本語化の文を書きます

```ruby:config/application.rb
module NaganoCake
  class Application < Rails::Application  
  :
  
  config.i18n.default_locale = :ja

  :
  end
end
```
ja.ymlファイルを作って下記を書きます
```yml:config/locales/ja.yml
ja:
  enums:
    activity:
      action_type:
        favorited_the_diary: "投稿にいいねしました"
        followed_you: 'あなたをフォローしました'
        commented_on_the_diary: '投稿にコメントしました'
```
以上で日本語になります
rails cでコンソールを開いて確認することが出来ます
action_typeに_i18nをつけると日本語で表示されます
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2821961/5c55095e-d82a-0b76-45a2-9ff67c99f349.png)

実装は以上です
表示できましたでしょうか


## まとめ
今回は通知機能をポリモーフィックを使って実装していきました
メソッドをそろえることでコードを変えずに表示を変えることが出来ます。


通知機能をいれるために覚えておくこと
* 通知用でモデルが必要
* 任意のタイミングで通知モデルをcreate

ポリモーフィックで覚えておくこと
* モデル情報ごと保存することが出来る


## 最後に
通知機能をポリモーフィックで作りましたが、ポリモーフィックで得られるメリットを最大限にすることを目的でコードをつくりました。redirect_pathは正直controllerにまとめて書いておいてもいいかなと思います。

polimorphic_pathという書き方もあるのですが私はrouteを変にカスタマイズしたせいで使えませんでした。確実にできる方法で書いてあります。興味のある方は調べてみてください。

ポリモーフィック自体はしっかり使えると便利だと思うので使いどころを考えていきたいですね。

いろんな書き方ができるため、どの方法が一番いいのかの判断をできるようにしたいです。