[Ruby on rails] 通知機能実装 コメント（非同期通信）の通知機能

<h2>初めに</h2>
今回、コメントの通知機能を実装しました。

参考記事はいくつかあるものの、私の場合、コメントもいいねは非同期通信にしてるため、
実装過程で、非同期通信が動かなくなったり、、なかなか大変でした。

「どの記事見ても何かしらのエラーが出る」という記事も見かけましたので、
あくまでも参考にして頂ければ...と思います。

<h2>完成イメージ</h2>

コメントしてくれた人&どの投稿にコメントしてくれたかがわかるようになっています。
名前も、記事名もリンクになっていますので、飛べます。
全削除で、全部の通知が消えます。
自分の記事に自分でコメントしても、通知はきません。
![スクリーンショット 2021-08-16 17.58.56.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1633266/785a7ed0-184a-0d5c-a736-4deecc81e947.png)


未読の通知があれば、NOTICEが赤いベルになります。既読したら、色は黒に戻ります。
![スクリーンショット 2021-08-16 17.58.19.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1633266/7abadd58-8335-254d-5fc4-92e5d13c3d30.png)
<h2>モデル作成/マイグレーションファイル作成</h2>

```
rails g model Notification
```

```
class CreateNotifications < ActiveRecord::Migration[5.2]
  def change
    create_table :notifications do |t|
      t.integer :visiter_id, null: false
      t.integer :visited_id, null: false
      t.integer :post_id
      t.string :action
      t.boolean :checked, default: false, null: false
      t.timestamps
    end
  end
end
```
通知を送る（コメントする）ユーザー```:visiter_id、```
受け取るユーザー```:visited_id、```
どの投稿でなのか```:post_id、```
```t.string :action```は、行動です。
いいね！の通知かコメントの通知かをここで、条件分岐させるのですが、
今回コメントの通知のみなので、正直いらないと思います！が、先のことを考え一応入れておきました...。
```t.boolean :checked　```は、既読したか未読かを判断します。
ブール型（trueかfalse）です。

<h2>ルーティング記述</h2>

```
resources :notifications, only: %i[index destroy]
```

<h2>モデル記述</h2>

```perl:user.rb
has_many :active_notifications, class_name: 'Notification', foreign_key: 'visiter_id', dependent: :destroy
 has_many :passive_notifications, class_name: 'Notification', foreign_key: 'visited_id', dependent: :destroy
```

フォロー、フォロワーの実装と似ています。
フォロー、フォロワーの時と同様に、２パターンの働きをするユーザーいます。
ユーザーと、通知の関係を考えてみると、
①通知を送るユーザーと、
②通知を受け取るユーザーの２種類いますのでこのように分けて定義します。
今回だと、```active_notifications```
```passive_notifications```です。それぞれ分けて書いてるけど、
結局はNotificationとのアソシエーションなので、
```class_name: 'Notification'```をつけてあげます。
<h6>class_nameは関連名と参照先のクラス名を異なるものにしたい場合に指定します！</h6>

```perl:post.rb
has_many :notifications, dependent: :destroy
```

post_comment_idは外部キーとして今回、持たせてないので、PostCommentモデルのアソシエーション定義はありません。

```perl:notification.rb
default_scope -> { order(created_at: :desc) }
  belongs_to :post
  belongs_to :visiter, class_name: 'User', foreign_key: 'visiter_id', optional: true
  belongs_to :visited, class_name: 'User', foreign_key: 'visited_id', optional: true
```

```default_scope -> { order(created_at: :desc) }```
並び順は、デフォルトで新しい順になります。
新着の通知から見れます。

<h2>コントローラー記述</h2>

非同期通信を実装済です。

```perl:post_comments_controller.rb
def create
    post = Post.find(params[:post_id])
    comment = current_user.post_comments.new(post_comment_params)
    comment.post_id = post.id
    comment.save
    @post = Post.find(params[:post_id])
    @post_comment = PostComment.new

以下が通知実装のために記載した箇所です。
    @post.create_notification_by(current_user)
  end
```
最後の一行で、引数を持ってポストモデルに飛ばします。

ちなみに、@post.create~の後に以下の記述をしている記事がありましたが
私は書かなくても、コメント通知、非同期通信は問題なく動いています。
```respond_to do |format|
      format.html { redirect_to request.referer }
      format.js
    end
```
↑上の記述については詳しく説明できません...
htmlかjsに渡すか〜？らしいのですが、。



```perl:notifications_controller.rb
class NotificationsController < ApplicationController
  def index
    @notifications = current_user.passive_notifications.(params[:page]).per(20)
    @notifications.where(checked: false).each do |notification|
      notification.update_attributes(checked: true)
    end
  end

  def destroy
    # 通知を全削除
    @notifications = current_user.passive_notifications.destroy_all
    redirect_to notifications_path
  end
end
```
passive_notificationsは受け取った通知です。
class_nameはNotificationですので、わかりやすく？？？？解釈すると
current_user.Notification.(params[:page]).per(20)です。
そう書くとエラーになるかとは思いますが。
```@notifications = current_user.passive_notifications.(params[:page]).per(20)```で、受け取った通知を全て取得します。
（ページネーション実装済です。）
```@notifications.where(checked: false).each do |notification|
      notification.update_attributes(checked: true)```
は、checkedがfalse=未読のものを取り出して、checkedをtrue=既読にします。
ちなみにここを、updateでもうまくいきます。
update_attributesの記述が多いのは何故なんでしょうか・・・。
update_attributesだと、値を複数同時に更新できるとのことですが、
今回はcheckedだけだし・・？




<h2>モデルファイル記述②</h2>

```perl:post.rb
 def create_notification_by(current_user)
    notification = current_user.active_notifications.new(
      post_id: id,
      visited_id: user_id,
      action: 'post_comment'
    )
if notification.visitor_id == notification.visited_id
      notification.checked = true
end
    
    notification.save if notification.valid?
  end
end
```

```if notification.visitor_id == notification.visited_id
      notification.checked = true
end```の部分は最後に付け加える方が良いと思います。
というのも、自分には通知がこないようにする記述なので、これ入れると、
通知できてるかどうかを、他のユーザーでログインして見ないといけないので面倒です。
自分の記事に対するコメントは、あらかじめchecked true＝既読にして、通知がこないようにします。



<h2>Viewファイル</h2>

```perl:notifications/index.html.erb
<div class="container">
    <div class='row justify-content-center mx-auto'>
        <div class="notification_box">
            <h4 class="text-center"><i class="fas fa-bell"></i>通知</h4>
             <p class="text-right"><%= link_to "全削除",notification_path(@notifications), method: :delete,class:"btn btn-light fas fa-trash" %></p>
        <% if @notifications.exists? %>
        <%= render @notifications %>
        <% else %>
        <p>通知はありません</p>
        <% end %>
　　　　</div>
 　　</div>
</div>
```

```perl:_notification.html.erb
<% visiter = notification.visiter %>
<% post = notification.post %>

<ul>
    <li><%= link_to user_path(visiter) do %><strong><%=visiter.name %><% end %></strong>さんが
    <strong><%= link_to post_path(notification.post_id) do %><%= post.title.truncate(20)%><% end %></strong>にコメントしました。
    <%= " (#{time_ago_in_words(notification.created_at)} 前)" %></li>
    
</ul>
```

time_ago_in_wordsはお決まりのものです。

https://railsdoc.com/page/time_ago_in_words


```perl:app/helpers/notifications_helper.rb
module NotificationsHelper

  def unchecked_notifications
    @notifications = current_user.passive_notifications.where(checked: false)
  end
end
```
helperファイルにこれまでコードを書いたことがなく、、
何故ここに？、何を書くのかわかりませんでした・・。

<h4>helperって何？？</h4>
form_withやlink_toもrailsのヘルパーです。
ヘルパーファイルに書くことで、それと同様に、ヘルパーで定義したメソッドを
viewで呼び出したりできる。使いこなせると、スッキリした開発につながるようです。
今回で言うと、unchecked_notificationsと書けば、
その中に定義している@notifications =〜が取り出せるんですね！

以下の記事がとてもわかりやすいと思いました！

https://qiita.com/yukiyoshimura/items/f0763e187008aca46fb4


```perl:_header.html.erb
<li class="nav-item">
       <% if unchecked_notifications.any? %>
         <li><%= link_to "Notice",notifications_path, class: "btn btn-light fas fa-bell",style: "color:tomato;"%>
      <% else %>
         <li><%= link_to "Notice",notifications_path, class: "btn btn-light fas fa-check"%>
      <% end %>
      </li>
```

<h2>参考にさせていただいた記事</h2>

https://qiita.com/yuto_1014/items/2db1dd4fcd7945b980f7


https://qiita.com/nekojoker/items/80448944ec9aaae48d0a

<h2>おわり</h2>

いいね　については、通知はせずに、
もらったいいね数をポイントとしてマイページに表示させたいと思います！！
QiitaでいうContributionみたいなやつです。