自分用にまとめます。
モデルの作成
$ rails g model Notification visiter_id:integer visited_id:integer
micropost_id:integer comment_id:integer action:string checked:boolean
20200724090751_create_notifications.rb
class CreateNotifications < ActiveRecord::Migration[5.2]
def change
create_table :notifications do |t|
# 通知を送ったユーザーのid
t.integer :visiter_id
# 通知を受け取ったユーザーのid
t.integer :visited_id
# いいねされた投稿のid
t.integer :item_id
# 投稿へのコメントのid
t.integer :comment_id
# 通知の種類(いいね、コメント、フォロー)
t.string :action
# 通知を確認したかどうか(初期値をfalseに設定)
t.boolean :checked, default: false, null: false
t.timestamps
end
end
end
$ rails db:migrate
モデルの関連付け
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
post.rb
has_many :notifications, dependent: :destroy
notification.rb
# 新しい通知が上に来るように指定
default_scope -> { order(created_at: :desc) }
belongs_to :post, optional: true
belongs_to :comment, optional: true
# 関連するカラム名(visiter_id/visited_id)とモデル名(User)を指定している
belongs_to :visiter, class_name: 'User', foreign_key: 'visiter_id', optional: true
belongs_to :visited, class_name: 'User', foreign_key: 'visited_id', optional: true
通知メソッド作成
いいね通知
post.rb
def create_notification_like(current_user)
# すでにいいねの通知をしたことがないかを検索
liked = Notification.where(visiter_id: current_user.id, visited_id: user_id, post_id: id, action: 'like')
# いいねの通知をしたことがなければ、新規通知作成
if liked.blank?
notification = current_user.active_notifications.new(post_id: id, visited_id: user_id, action: 'like')
# 自分の投稿に対するいいねは通知が行かないようにする
if notification.visiter_id == notification.visited_id
notification.checked = true
end
notification.save if notification.valid?
end
end
コメント通知
post.rb
def create_notification_comment(current_user, comment_id)
# 投稿にコメントした自分以外のユーザーをすべて取得し、全員に通知を送る
commented_ids = Comment.select(:user_id).where(post_id: id).where.not(user_id: current_user.id).distinct
commented_ids.each do |commented_id|
save_notification_comment(current_user, comment_id, commented_id['user_id'])
end
# まだ誰もコメントしていない場合は、投稿者に通知を送る
save_notification_comment(current_user, comment_id, user_id) if commented_id.blank?
end
def save_notification_comment(current_user, comment_id, visited_id)
# コメントは複数回することがあるため、複数回通知する
notification = current_user.active_notifications.new(post_id: id, comment_id: comment_id, visited_id: visited_id, action: 'comment')
# 自分の投稿に対するコメントの場合は、通知済みとする
if notification.visiter_id == notification.visited_id
notification.checked = true
end
notification.save if notification.valid?
end
フォロー通知
user.rb
def create_notification_follow!(current_user)
# すでにフォローの通知をしたことがないかを検索
followed = Notification.where(visiter_id: current_user.id, visited_id: id, action: 'follow')
# いいねの通知をしたことがなければ、新規通知作成
if followed.blank?
notification = current_user.active_notifications.new(visited_id: id, action: 'follow')
notification.save if notification.valid?
end
end
コントローラで定義
いいね
likes_controller.rb
def create
@post = Post.find(params[:post_id])
unless @post.liked_by?(current_user)
@like = current_user.likes.new(post_id: @post.id)
@like.save
# 追記
@post.create_notification_like(current_user)
end
end
コメント
comments_controller.rb
def create
@post = Post.find(params[:post_id])
@comment = @post.comments.new(comment_params)
@comment.user_id = current_user.id
@comments = @post.comments.all
if @comment.save
# 追記
@post.create_notification_comment(current_user, @comment.id)
render :create
else
@post = Post.find(params[:post_id])
@comments = @post.comments.all
render 'posts/show'
end
end
フォロー
relationships_controller.rb
def follow
@user = User.find(params[:id])
current_user.follow(params[:id])
#追記
@user.create_notification_follow(current_user)
@posts = @user.posts.all
render :create
end
コントローラの作成
$ rails g controller notifications
config/routes.rb
resources :notifications, only: :index
notifications_controller.rb
class NotificationsController < ApplicationController
def index
# current_userが受け取った通知一覧
@notifications = current_user.passive_notifications
# @notificationの中でまだ確認していない通知
@notifications.where(checked: false).each do |notification|
notification.update_attributes(checked: true)
end
end
def destroy_all
#通知を全削除
@notifications = current_user.passive_notifications.destroy_all
redirect_to notifications_path
end
end
ビューの作成
notifications/index.html.slim
.notification-visual
.container
.row
.col-lg-5
.col-lg-2.text-center
h4.mt-5.mb-4.font-weight-bold.notification-caption 通知
.row
.col-lg-3
.col-lg-6
.frame-notification
= link_to notifications_destroy_all_path, method: :delete do
i.fas.fa-trash style="color: black;"
h7 style="color: black;" 全削除
<hr>
- notifications = @notifications.where.not(visiter_id: current_user.id)
- if notifications.exists?
# _notification.html.slimファイルにパーシャルで飛ばす
= render notifications
- else
p 通知はありません
notifications/_notification.html.slim
- visiter = notification.visiter
- visited = notification.visited
.form-inline
span
= link_to user_path(visiter) do
= attachment_image_tag visiter, :image, format: 'jpeg', fallback: "noimage.png", size: "40x30", class: "mr-2"
= visiter.name
= ' さんが'
- case notification.action
- when 'follow' then
= "あなたをフォローしました"
- when 'like' then
= link_to 'あなたの投稿', notification.post, style: "font-weight: bold;"
= "にいいねしました"
- when 'comment' then
- if notification.post.user_id == visited.id
= link_to "あなたの投稿", notification.post, style: "font-weight: bold;"
- else
span
= link_to post_path(notification.post) do
= attachment_image_tag notification.post.user, :image, format: 'jpeg', fallback: "noimage.png", size: "25x20", class: "mr-1 ml-1"
strong
= notification.post.user.name + 'さんの投稿'
= "にコメントしました"
p.text-muted.mb-0
= Comment.find_by(id: notification.comment_id)&.comment
.small.text-muted.text-right
= time_ago_in_words(notification.created_at).upcase
hr
通知がきたら通知のボタン部分に丸いアイコンを表示させる
layout/_user.html.slim
-if unchecked_notifications.any?
li.btn.btn-light.home-btn= link_to notifications_path, class: 'list' do
i.fas.fa-circle.n-circle
i.fas.fa-bell
| 通知
-else
li.btn.btn-light.home-btn= link_to notifications_path, class: 'list' do
i.fas.fa-bell
| 通知
notifications_helper.rb
module NotificationsHelper
def unchecked_notifications
@notifications = current_user.passive_notifications.where(checked: false)
end
end
完成!