#はじめに
今回は通知機能を作成しました。
こちらの記事を参考にさせていただいております。
https://qiita.com/nekojoker/items/80448944ec9aaae48d0a
https://qiita.com/yuto_1014/items/2db1dd4fcd7945b980f7
###作るもの
コメント・いいね・フォローされた時に通知を作成して確認・削除できる機能を作成します。すでに上記の機能はあるものとして作成してます。どれか一つでも機能があればOK。参考に過去に投稿したコメント・フォロー機能についてリンクを載せておきます。
コメント機能:https://qiita.com/E6YOteYPzmFGfOD/items/ef776d34908872ea19f7
フォロー機能:https://qiita.com/E6YOteYPzmFGfOD/items/ec0492b509962c3b7ae4
#ER図
参考に今回作成する通知機能のER図です。(今回関係ないタグ機能がありますが無視してください。)
#作成の流れ
1.Notificationモデルの作成
2.各モデルの関連づけ
3.通知メゾットの作成
4.通知機能をコントローラーへ埋め込む
5.通知一覧画面と通知削除作成
#モデルの作成
rails g model Notification visitor:references visited:references micropost:references comment:references action:string checked:boolean
class CreateNotifications < ActiveRecord::Migration[5.1]
def change
create_table :notifications do |t|
t.references :visitor, foreign_key:{ to_table: :users }, null: false
t.references :visited, foreign_key:{ to_table: :users }, null: false
t.references :micropost, foregin_key: true
t.references :comment, foregin_key: true
t.string :action, null: false
t.boolean :checked, null: false, default: false
t.timestamps
end
end
end
vistor_id
→通知するユーザー(いいね、コメント、フォローする側)
visted_id
→通知をうけるユーザー
rails g model カラム名:references とすると自動で
・カラム名にidをつけた形でカラムを作成。
・インデックス付与。
foreign_key:true
・外部キー制約を付与
to_table: :user
・visitorとvisitedカラムの参照テーブルを指定してます。
nul: false
・空の状態で保存できなくする。
#モデルの関連付け
has_many :active_notifications, foreign_key:"visitor_id", class_name: "Notification", dependent: :destroy
has_many :passive_notifications, foreign_key:"visited_id", class_name: "Notification", dependent: :destroy
has_many :notifications, dependent: :destroy
has_many :notifications, dependent: :destroy
class Notification < ApplicationRecord
default_scope -> { order(created_at: :desc) }
belongs_to :visitor, class_name: "User", optional: true
belongs_to :visited, class_name: "User", optional: true
belongs_to :micropost, optional: true
belongs_to :comment, optional: true
validates :visitor_id, presence: true
validates :visited_id, presence: true
ACTION_VALUES = ["like", "follow", "comment"]
validates :action, presence: true, inclusion: {in:ACTION_VALUES}
validates :checked, inclusion: {in: [true,false]}
end
各オプション
foreign_key:"vistor_id", class_name: "Notification"
→関連づけるカラム名とテーブル名を指定します。
dependent: :destroy
→投稿が削除された際に関連付いている通知を削除。
default_scope -> { order(created_at: :desc) }
→通知が新しい順に並ぶ。
optional: true: nullの値を許可する。
inclusion
→保存できる値を制限しています。
#通知メゾットの作成
###いいね
def create_notification_like!(current_user)
temp = Notification.where(["visitor_id = ? and visited_id = ? and micropost_id = ? and action = ? ",
current_user.id, user_id, id, 'like'])
if temp.blank?
notification = current_user.active_notifications.new(
micropost_id: id,
visited_id: user_id,
action: 'like'
)
if notification.visitor_id == notification.visited_id
notification.checked = true
end
notification.save if notification.valid?
end
end
始めに通知が作成済みでないか確認してます。(何度も作成しないように)登録済みの場合と自分の投稿に対するいいねの場合は通知を確認済みとして作成します。(checkedをtrueにする。)active_notificationsはuserモデルでhas_many関連付けを行った際に名付けた関連名です。
###コメント
def create_notification_comment!(current_user, comment_id)
#同じ投稿にコメントしているユーザーに通知を送る。(current_userと投稿ユーザーのぞく)
temp_ids = Comment.where(micropost_id: id).where.not("user_id=? or user_id=?", current_user.id,user_id).select(:user_id).distinct
#取得したユーザー達へ通知を作成。(user_idのみ繰り返し取得)
temp_ids.each do |temp_id|
save_notification_comment!(current_user, comment_id, temp_id['user_id'])
end
#投稿者へ通知を作成
save_notification_comment!(current_user, comment_id, user_id)
end
def save_notification_comment!(current_user, comment_id, visited_id)
notification = current_user.active_notifications.new(
micropost_id: id,
comment_id: comment_id,
visited_id: visited_id,
action: 'comment'
)
if notification.visitor_id == notification.visited_id
notification.checked = true
end
notification.save if notification.valid?
end
コメントの場合は同じ投稿に対してコメントしているユーザーにもコメントを送るようにします。マイクロポストの投稿者には必ず通知が1件作成されるようにします。
「where.(micropost_id: id)」
→同じ投稿にコメント
「where.not("user_id=? or user_id=?", current_user.id,user_id)」
→コメントしたユーザーと投稿者は除く。
distinct
→複数回コメントしている人も通知は一件のみにする
select(:user_id)
→user_idのみ取得しています。
取得したuser_idを下で定義している通知作成メゾットに渡して繰り返し通知を作成します。
###フォロー
def create_notification_follow!(current_user)
#すでに通知が作成されているか確認
temp = Notification.where(["visitor_id = ? and visited_id = ? and action = ? ",current_user.id, id, 'follow'])
if temp.blank?
notification = current_user.active_notifications.new(
visited_id: id,
action: 'follow'
)
notification.save if notification.valid?
end
end
#通知機能を各コントローラーへ埋め込み
各通知メゾットをコメント等のアクションを起こした際に起動するようにコントローラー内へ埋め込んでいきます。
###コメント
def create
@comment = current_user.comments.build(comment_params)
@comment.micropost_id = params[:micropost_id]
if @comment.save
flash[:success] = 'コメントしました'
#通知機能用
@micropost=@comment.micropost
@micropost.create_notification_comment!(current_user, @comment.id)
#ここまで通知機能
redirect_to @comment.micropost
else
comments_get
render template: 'microposts/show'
end
###フォロー
def create
@user =User.find(params[:follow_relationship][:following_id])
current_user.follow(@user)
#通知機能追加
@user.create_notification_follow!(current_user)
respond_to do |format|
format.html {redirect_back(fallback_location: root_url)}
format.js
end
end
###いいね
def create
@user = current_user
@micropost = Micropost.find(params[:micropost_id])
current_user.like(@micropost)
#通知機能追加
@micropost.create_notification_like!(current_user)
respond_to do |format|
format.html { redirect_back(fallback_location: root_url) }
format.js
end
end
#通知一覧画面と通知削除作成
まずは通知ページ表示ようのコントローラ、ルーディングの設定です。
rails g controller notifications
resources :notifications, only: [:index, :destroy]
def index
@notifications = current_user.passive_notifications
#通知画面を開くとcheckedをtrueにして通知確認済にする
@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
続いて通知一覧画面の作成です。
<h3 class="text-center">通知</h3>
<%= link_to "通知削除", notification_path(@notifications), method: :delete ,class: "fas fa-trash" %>
<% if @notifications.exists? %>
<div class="notification-index">
<%= render @notifications %>
</div>
<% else %>
<p>通知はありません</p>
<% end %>
<div class="notification-view">
<%= notification_form(notification) %><span class="moderate-font"><%= " (#{time_ago_in_words(notification.created_at)}前)" %></span>
<br>
<% if !@comment.nil? %>
<p class="notification-comment"><%= @comment %></p>
<% end %>
</div>
いいね、フォロー、コメントの通知によって表示する内容を変更するために、
ヘルパーメゾットを作成してviewで呼び出します。
module NotificationsHelper
def notification_form(notification)
#通知を送ってきたユーザーを取得
@visitor = notification.visitor
#コメントの内容を通知に表示する
@comment = nil
@visitor_comment = notification.comment_id
# notification.actionがfollowかlikeかcommentかで処理を変える
case notification.action
when 'follow'
#aタグで通知を作成したユーザーshowのリンクを作成
tag.a(notification.visitor.name, href: user_path(@visitor)) + 'があなたをフォローしました'
when 'like'
tag.a(notification.visitor.name, href: user_path(@visitor)) + 'が' + tag.a('あなたの投稿', href: micropost_path(notification.micropost_id)) + 'にいいねしました'
when 'comment' then
#コメントの内容と投稿のタイトルを取得
@comment = Comment.find_by(id: @visitor_comment)
@comment_content =@comment.content
@micropost_title =@comment.micropost.title
tag.a(@visitor.name, href: user_path(@visitor)) + 'が' + tag.a("#{@micropost_title}", href: micropost_path(notification.micropost_id)) + 'にコメントしました'
end
end
以上となります。
だいぶ長くなりましたがここまでお読みいただきありがとうございました!!