#はじめに
通知機能の復習のため残しておきます。
#手順
###前提
いいね/コメント/フォロー/DM機能はそれぞれ作成済みとします。
今回は、
- post(投稿)に対するいいね、コメント
- user同士のフォロー
- フォローユーザー同士であればdm可能
という条件で機能を作成しています。
###やったこと
まず、通知の流れとして、
1.いいね/コメント/フォロー/DMのcreate時に、独自のメソッドで通知を作成
2.独自のメソッドが定義されているモデルへ行き、指定された処理を行う
いいね、コメント、フォロー、DMの順で記します。
###いいね通知
favorite_controller.rb
@post = Post.find(params[:post_id]) #いいねをする投稿を探す
favorite = current_user.favorites.new(post_id: @post.id) #いいねを作成
favorite.save
@post.create_notification_favorite!(current_user) #いいねの通知を作成
post.rb
def create_notification_favorite!(current_user)
favorite_exist = Notification.where("visiter_id = ? and visited_id = ? and post_id = ? and action = ? ", current_user.id, user.id, id, 'favorite') # いいねしているか検索
if favorite_exist.blank? # いいねしていない場合は通知を作成
notification = current_user.active_notifications.new(
post_id: id,
visited_id: user_id, #通知相手に相手のidを指定
action: 'favorite', #helperで後ほど使うためわかりやすい名前をつける
checked: false #通知確認済み=true, 未確認=falseであり、defaultでfalseを設定しておく
)
notification.save if notification.valid?
end
end
###コメント通知
post_comment_controller.rb
def create
@post = Post.find(params[:post_id])
@post_comment = PostComment.new(post_comment_params)
@post_comment.post_id = @post.id
@post_comment.user_id = current_user.id
@comment_post = @post_comment.post
if @post_comment.save
@comment_post.create_notification_comment!(current_user, @post_comment.id) #コメントの通知を作成
@post_comment = PostComment.new(post_comment_params)
end
end
post.rb
def create_notification_comment!(current_user, comment_id)
comment_users = PostComment.select(:user_id).where(post_id: id).where.not(user_id: current_user.id).distinct # 自分以外にコメントしている人をすべて取得し、全員に通知を送る
comment_users.each do |comment_user|
save_notification_comment!(current_user, comment_id, comment_user['user_id']) if comment_users.blank?
end
save_notification_comment!(current_user, comment_id, user_id) if comment_users.blank? # まだ誰もコメントしていない場合は、投稿者に通知を送る
end
def save_notification_comment!(current_user, comment_id, visited_id)
# 複数回のコメントを考慮。1つの投稿に複数回通知する
notification = current_user.active_notifications.new(
post_id: id,
comment_id: comment_id,
visited_id: visited_id,
action: 'comment',
checked: false
)
notification.save! if notification.valid?
end
###フォロー通知
relationships_controller.rb
def create
@user = User.find(params[:user_id])
current_user.follow(params[:user_id])
@user.create_notification_follow!(current_user) #フォロー通知を作成
end
user.rb
def create_notification_follow!(current_user)
follow_exist = Notification.where(["visiter_id = ? and visited_id = ? and action = ? ", current_user.id, id, 'follow'])
if follow_exist.blank?
notification = current_user.active_notifications.new(
visited_id: id,
action: 'follow',
checked: false
)
notification.save if notification.valid?
end
end
###DM通知
usersとrooms(チャットルーム)を繋ぐ中間テーブルとして、
chats(メッセージとuserを紐付けて保管)
user_rooms(チャットルームに入るユーザー2名を紐付けて保管)
としています。
今回は、roomsで行うべき処理をchatsで行なっているので少し違和感ですが、とりあえず機能はしています。
chats_controller.rb
@chat = current_user.chats.new(chat_params)
@room = @chat.room
@chat.save
@chat.room.create_notification_dm!(current_user, @chat.room_id, @chat.id)
@chats = @room.chats
@chat = Chat.new(room_id: chat_params[:room_id])
room.rb
def create_notification_dm!(current_user, room_id, chat_id)
dm_user = UserRoom.select(:user_id).where(room_id: room_id).where.not(user_id: current_user.id).distinct
dm_user.each do |dm_user|
save_notification_dm!(current_user, chat_id, dm_user['user_id'], room_id)
end
# 初めてメッセージを送った場合
if dm_user.blank?
visited_id = UserRoom.where(room_id: room_id).where.not(user_id: current_user.id).distinct.first.user_id
save_notification_dm!(current_user, chat_id, visited_id, room_id)
end
end
def save_notification_dm!(current_user, chat_id, visited_id, room_id)
# 複数回のコメントを考慮。1つの投稿に複数回通知する
notification = current_user.active_notifications.new(
room_id: room_id,
chat_id: chat_id,
visited_id: visited_id,
action: 'dm',
checked: false
)
notification.save! if notification.valid?
end
###通知一覧を表示
notifications_helper.rb
def notification_form(notification)
@visiter = notification.visiter
@comment = nil
@chat = nil
#notification.actionがfollowか、favoriteか、commentか、dmか
case notification.action
when "follow" then
tag.a(notification.visiter.nickname, href:user_path(@visiter), style:"font-weight: bold;")+"があなたをフォローしました"
when "favorite" then
tag.a(notification.visiter.nickname, href:user_path(@visiter), style:"font-weight: bold;")+"が"+tag.a('あなたの投稿', href:post_path(notification.post_id), style:"font-weight: bold;")+"にいいねしました"
when "comment" then
@comment = PostComment.find_by(id: notification.comment_id)&.comment
tag.a(@visiter.nickname, href:user_path(@visiter), style:"font-weight: bold;")+"が"+tag.a('あなたの投稿', href:post_path(notification.post_id), style:"font-weight: bold;")+"にコメントしました"
when "dm" then
@chat = Chat.find_by(id: notification.chat_id)&.message
tag.a(@visiter.nickname, href:user_path(@visiter), style:"font-weight: bold;")+"があなたに"+tag.a('メッセージ',href:chat_path(notification.visiter), style:"font-weight: bold;")+"を送りました"
end
end
def unchecked_notifications
@notifications = current_user.passive_notifications.where(checked: false)
end
notifications_controller.rb
def index
@notifications = Notification.where(visited_id: current_user.id, checked: false).where.not(visiter_id: current_user.id).to_a
# 通知の確認済みをtrueに変更して保存したいため、.to_aで配列とし、ビューでは表示可能に。処理としてはchecked: trueにするところまで行っており、リロードすると確認ずみとなる。
@notifications.each do |notification|
notification.update_attributes(checked: true)
@comment = PostComment.find_by(id: notification.comment_id)
end
end
def destroy
notification = Notification.find(params[:id])
notification.destroy
redirect_back(fallback_location: root_path)
end
def destroy_all
@checked_notifications = Notification.where(visited_id: current_user.id, checked: true).where.not(visiter_id: current_user.id)
@checked_notifications.destroy_all
redirect_back(fallback_location: root_path)
end
notifications/index.html.erb
<div id="tab-contents">
<div id="tab1" class="tab notifications-index">
<% if @notifications.present? %>
<table class="table table-borderless">
<% @notifications.each do |notification| %> #@notifications(未確認の通知たち)があればeachで表示
<tr>
<td>
<%= link_to user_path(notification.visiter) do %>
<%= attachment_image_tag notification.visiter, :profile_image, :fill,65,65, format: "jpeg", fallback: "no_image.png", size: "65x65", class: "rounded-circle" %>
<% end %>
</td>
<td>
<p><%= notification_form(notification) %><span class="moderate-font"><%= " (#{time_ago_in_words(notification.created_at)} 前)" %></span></p>
</td>
</tr>
<% end %>
</table>
<% else %>
<p>未読の通知はありません</p>
<% end %>
</div>
</div>
お好みで、@notificationsの中身を既読済みに変えてアレンジすると使いやすくなるかと。
#終わりに
記述量は多いですが、一つひとつ理解することで応用できるように。
とはいえまだ疑問点は残るため、解消できるよう頑張るのみ。