【Rails DM】通知機能を作ろう!
ステップ
###1:DM機能を実装しよう
この記事を参考に作ってみよう!
###2:通知機能を実装しよう
####2−1:モデルを作成しよう
rails g model Notification visitor_id:integer visited_id:integer room_id:integer message_id:integer action:string checked:boolean
####2−2:作成した通知モデルを、User、Post、Commentと紐付け
UserモデルとNotificationモデルとの関連付け
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モデルとの関連付け
has_many :notifications, dependent: :destroy
MessageモデルとNotificationモデルとの関連付け
has_many :notifications, dependent: :destroy
NotificationモデルとUser,Room,Messageモデルとの関連付け
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
####2−3:DM通知の作成メソッド
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
####2−4:通知の一覧画面の作成
rails g controller notifications index
class NotificationsController < ApplicationController
def index
@notifications = current_user.passive_notifications
end
end
<% notifications = @notifications.where.not(visitor_id: current_user.id) %>
<% if notifications.exists? %>
<%= render notifications %>
<% else %>
<p>通知はございません</p>
<% end %>
<% visitor = notification.visitor %>
<% visited = notification.visited %>
<div>
<%= link_to user_path(visitor) do %>
<%= visitor.name %>さんが
<% end %>
<% if notification.action=='dm' %>
あなたにDMを送りました
<% end %>
</div>
おまけ
メッセージ一覧をLINEのように、わかりやすい表示にする
まず、どうやったら実装できそうかイメージしてみよう!
1:自分発信のメッセージから、相手発信のメッセージかを判定する
2:自分発信のメッセージと、相手発信のメッセージにそれぞれ異なるCSSを当てる
この2ステップで実装できそう!
肝心なのはステップ1。
どうやって判定しよう。
そうだ。メッセージ機能を作っているMessageテーブルにuser_idカラムがあったはず。
(誰からの投稿か判断するために予め存在した)
そこに入っている値によって、自分発信のメッセージから、相手発信のメッセージかを判定することができそう!
故いに次の章で実際に試してみよう!
実際に実装してみよう!
<% @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例)
.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
<% 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!
class NotificationsController < ApplicationController
def index
@notifications = current_user.passive_notifications
# ↓この下に何か書いてある人、特にcheckをtrueにするような処理を書いている人はその記述は消しましょう!
end
end
ステップ3
<% 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を引数にする。
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】通知機能を誰でも実装できるように解説する【いいね、コメント、フォロー】