【Rails DM】DMが送信された時の通知機能を作ろう！

# 【Rails DM】通知機能を作ろう！
## ステップ
###１：DM機能を実装しよう
[この記事を参考に作ってみよう！](https://qiita.com/tenitiumai/items/3d9466d7a24197f690bb)
###２：通知機能を実装しよう
####２−１：モデルを作成しよう

```ruby:ruby
rails g model Notification visitor_id:integer visited_id:integer room_id:integer message_id:integer action:string checked:boolean
```

####２−２：作成した通知モデルを、User、Post、Commentと紐付け
UserモデルとNotificationモデルとの関連付け

```ruby:app/models/user.rb
has_many :active_notifications, class_name: 'Notification', foreign_key: 'visitor_id', dependent: :destroy
has_many :passive_notifications, class_name: 'Notification', foreign_key: 'visited_id', dependent: :destroy
```

RoomモデルとNotificationモデルとの関連付け

```ruby:app/models/room.rb
has_many :notifications, dependent: :destroy
```

MessageモデルとNotificationモデルとの関連付け

```ruby:app/models/message.rb
has_many :notifications, dependent: :destroy
```

NotificationモデルとUser,Room,Messageモデルとの関連付け

```ruby:app/models/notification.rb
  default_scope -> { order(created_at: :desc) }

  belongs_to :room, optional: true
  belongs_to :message, optional: true

  belongs_to :visitor, class_name: 'User', foreign_key: 'visitor_id', optional: true
  belongs_to :visited, class_name: 'User', foreign_key: 'visited_id', optional: true
```
####２−３：DM通知の作成メソッド

```ruby:messages_controller.rb
class MessagesController < ApplicationController
    def create
        if Entry.where(user_id: current_user.id, room_id: params[:message][:room_id]).present?
          @message = Message.new(message_params)
          # ここから
          @room=@message.room
          # ここまでを追加
          if @message.save

            # ここから
            @roommembernotme=Entry.where(room_id: @room.id).where.not(user_id: current_user.id)
            @theid=@roommembernotme.find_by(room_id: @room.id)
            notification = current_user.active_notifications.new(
                room_id: @room.id,
                message_id: @message.id,
                visited_id: @theid.user_id,
                visitor_id: current_user.id,
                action: 'dm'
            )
            # 自分の投稿に対するコメントの場合は、通知済みとする
            if notification.visitor_id == notification.visited_id
                notification.checked = true
            end
            notification.save if notification.valid?
            # ここまでを追加

            redirect_to "/rooms/#{@message.room_id}"
          end
        else
          redirect_back(fallback_location: root_path)
        end
    end
  
    private 
      def message_params
          params.require(:message).permit(:user_id, :body, :room_id).merge(user_id: current_user.id)
      end
end

```


####２−４：通知の一覧画面の作成

```ruby:terminal
rails g controller notifications index
```

```ruby:controller/notifications_controller.rb
class NotificationsController < ApplicationController
  def index
    @notifications = current_user.passive_notifications
  end
end
```

```erb:views/notifications/index.html.erb
<% notifications = @notifications.where.not(visitor_id: current_user.id) %>
<% if notifications.exists? %>
    <%= render notifications %>
<% else %>
    <p>通知はございません</p>
<% end %>
```

```erb:views/notifications/_notificastion.html.erb
<% visitor = notification.visitor %>
<% visited = notification.visited %>
<div>
 <%= link_to user_path(visitor) do %>
     <%= visitor.name %>さんが
 <% end %>
 <% if notification.action=='dm' %>
    あなたにDMを送りました
 <% end %>
</div>
```
## おまけ

### メッセージ一覧をLINEのように、わかりやすい表示にする
![スクリーンショット 2020-09-15 18.31.50.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/411851/20816daf-4860-33fc-281a-38270b144c0b.png)

#### まず、どうやったら実装できそうかイメージしてみよう！
1：自分発信のメッセージから、相手発信のメッセージかを判定する
2：自分発信のメッセージと、相手発信のメッセージにそれぞれ異なるCSSを当てる

この2ステップで実装できそう！
肝心なのはステップ1。

どうやって判定しよう。
そうだ。メッセージ機能を作っているMessageテーブルにuser_idカラムがあったはず。
（誰からの投稿か判断するために予め存在した）
そこに入っている値によって、自分発信のメッセージから、相手発信のメッセージかを判定することができそう！
故いに次の章で実際に試してみよう！

#### 実際に実装してみよう！

```erb:rooms/show.html.erb
<% @messages.each do |m| %> <%# ここのインスタンス変数は皆さんのお使いのインスタンス変数に直してくださいね！ %>
    <% if m.user_id == current_user.id %>
        <div class="current_user">
            <strong class="current_user"><%= m.body %></strong>
            <small class="current_user"><%= m.user.name %>さん</small>
        </div>
    <% else %>
        <div class="visited_user">
            <strong class="visited_user"><%= m.body %></strong>
            <small class="visited_user"><%= m.user.name %>さん</small>
        </div>
    <% end %>
<% end %>
```

上でやっていることはわかりますでしょうか？！
user_idの値がログインユーザーのidならばcurrent_userというclassをメッセージの箱（div）に、
user_idの値が相手のユーザーのid(上では厳密に言えば、ログインユーザー以外のidという風にしております)ならばvisited_userというclassをメッセージの箱（div）に、
付与しております！

CSS例）

```css:css
.current_user {
    color: red;
    text-align: right;
    background-color: #fff;
    width: fit-content;
    margin: 10px 0 0 auto;
    border-radius: 30px;
    -webkit-border-radius: 30px;
    -moz-border-radius: 30px;
    -ms-border-radius: 30px;
    -o-border-radius: 30px;
    box-sizing: border-box;
    padding: 10px;
}

.visited_user {
    color: #fff;
    margin: 10px;
    background-color: brown;
    text-align: left;
    width: fit-content;
    margin: 10px auto 0 0;
    border-radius:30px;
    -webkit-border-radius:30px;
    -moz-border-radius:30px;
    -ms-border-radius:30px;
    -o-border-radius:30px;
    box-sizing: border-box;
    padding: 10px;
}

```


### 通知一覧の通知を、既読したら通知から消えるようにする
#### まず、どうやったら実装できそうかイメージしてみよう！
前提：通知機能はNotificationモデルで実装
そのカラムにはid, visitor_id, visited_id, room_id, message_id, action, checked, created_at, updated_at
checkedカラム(boolean型)で既読未読を管理予定

1：通知を未読のもののみ表示するように(即ち、checkedカラムがfalseのもののみ表示)
2：最初、デフォルトでは未読状態にする
3：「既読にする」リンクを押せば、checkedカラムをtrueにすることで、既読状態にする

以上のステップを踏めば、実装できそう！

#### 実際に実装してみよう！
#####まずステップ1

```erb:_notification.html.erb
<% if notification.checked == false %> <%# ←この1行が大事 %>
    <div>
        <%= link_to room_path(@roomId) do %>
            <%= visitor.name %>さん
        <% end %>
        <% if notification.action=='dm' %>
        があなた(<%= visited.name %>さん)にDMを送りました
        <% end %>
        <%= link_to '既読にする',notification_path(notification.id),method: :put %>
    </div>
<% end %> <%# ←この1行が大事 %>
```

##### ステップ2

こちらそもそもcheckedカラムはfalseにしているので変更しないでOK!

```rb:_notification.html.erb
class NotificationsController < ApplicationController
  def index
    @notifications = current_user.passive_notifications
    # ↓この下に何か書いてある人、特にcheckをtrueにするような処理を書いている人はその記述は消しましょう！
  end
end
```

##### ステップ3

```erb:_notification.html.erb
<% if notification.checked == false %>
    <div>
        <%= link_to room_path(@roomId) do %>
            <%= visitor.name %>さん
        <% end %>
        <% if notification.action=='dm' %>
        があなた(<%= visited.name %>さん)にDMを送りました
        <% end %>
        <%= link_to '既読にする',notification_path(notification.id),method: :put %>
        <%# ↑この1行！！ %>
    </div>
<% end %>
```

『既読にする』を押したときに、notificationコントローラーのupdateアクションを呼ぶようにする。
その際にパラメーターとしてNotificationテーブルのidを引数にする。

```rb:notification_controller
class NotificationsController < ApplicationController
  # 省略

  def update
    notification=Notification.find(params[:id]) #...①
    if notification.update(checked: true) #...②
      redirect_to action: :index
    end
  end
end
```
そして、①によって、パラメーラーとして渡されたidを下にNotificationテーブルの該当idをもつレコードを引っ張り出してくる。
最後に②によって、そのレコードのcheckedカラムの値をtrueにする

これで実装できたはずです！！！
##参考記事
[【Rails】通知機能を誰でも実装できるように解説する【いいね、コメント、フォロー】](https://qiita.com/nekojoker/items/80448944ec9aaae48d0a)