初めに
今回、コメントの通知機能を実装しました。参考記事はいくつかあるものの、私の場合、コメントもいいねは非同期通信にしてるため、
実装過程で、非同期通信が動かなくなったり、、なかなか大変でした。
「どの記事見ても何かしらのエラーが出る」という記事も見かけましたので、
あくまでも参考にして頂ければ...と思います。
完成イメージ
コメントしてくれた人&どの投稿にコメントしてくれたかがわかるようになっています。
名前も、記事名もリンクになっていますので、飛べます。
全削除で、全部の通知が消えます。
自分の記事に自分でコメントしても、通知はきません。
未読の通知があれば、NOTICEが赤いベルになります。既読したら、色は黒に戻ります。
モデル作成/マイグレーションファイル作成
rails g model Notification
class CreateNotifications < ActiveRecord::Migration[5.2]
def change
create_table :notifications do |t|
t.integer :visiter_id, null: false
t.integer :visited_id, null: false
t.integer :post_id
t.string :action
t.boolean :checked, default: false, null: false
t.timestamps
end
end
end
通知を送る(コメントする)ユーザー:visiter_id、
受け取るユーザー:visited_id、
どの投稿でなのか:post_id、
t.string :action
は、行動です。
いいね!の通知かコメントの通知かをここで、条件分岐させるのですが、
今回コメントの通知のみなので、正直いらないと思います!が、先のことを考え一応入れておきました...。
t.boolean :checked
は、既読したか未読かを判断します。
ブール型(trueかfalse)です。
ルーティング記述
resources :notifications, only: %i[index destroy]
モデル記述
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
フォロー、フォロワーの実装と似ています。
フォロー、フォロワーの時と同様に、2パターンの働きをするユーザーいます。
ユーザーと、通知の関係を考えてみると、
①通知を送るユーザーと、
②通知を受け取るユーザーの2種類いますのでこのように分けて定義します。
今回だと、active_notifications
passive_notifications
です。それぞれ分けて書いてるけど、
結局はNotificationとのアソシエーションなので、
class_name: 'Notification'
をつけてあげます。
class_nameは関連名と参照先のクラス名を異なるものにしたい場合に指定します!
has_many :notifications, dependent: :destroy
post_comment_idは外部キーとして今回、持たせてないので、PostCommentモデルのアソシエーション定義はありません。
default_scope -> { order(created_at: :desc) }
belongs_to :post
belongs_to :visiter, class_name: 'User', foreign_key: 'visiter_id', optional: true
belongs_to :visited, class_name: 'User', foreign_key: 'visited_id', optional: true
default_scope -> { order(created_at: :desc) }
並び順は、デフォルトで新しい順になります。
新着の通知から見れます。
コントローラー記述
非同期通信を実装済です。
def create
post = Post.find(params[:post_id])
comment = current_user.post_comments.new(post_comment_params)
comment.post_id = post.id
comment.save
@post = Post.find(params[:post_id])
@post_comment = PostComment.new
以下が通知実装のために記載した箇所です。
@post.create_notification_by(current_user)
end
最後の一行で、引数を持ってポストモデルに飛ばします。
ちなみに、@post.create~の後に以下の記述をしている記事がありましたが
私は書かなくても、コメント通知、非同期通信は問題なく動いています。
format.html { redirect_to request.referer }
format.js
end
↑上の記述については詳しく説明できません...
htmlかjsに渡すか〜?らしいのですが、。
class NotificationsController < ApplicationController
def index
@notifications = current_user.passive_notifications.(params[:page]).per(20)
@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
end
passive_notificationsは受け取った通知です。
class_nameはNotificationですので、わかりやすく????解釈すると
current_user.Notification.(params[:page]).per(20)です。
そう書くとエラーになるかとは思いますが。
@notifications = current_user.passive_notifications.(params[:page]).per(20)
で、受け取った通知を全て取得します。
(ページネーション実装済です。)
notification.update_attributes(checked: true)```
は、checkedがfalse=未読のものを取り出して、checkedをtrue=既読にします。
ちなみにここを、updateでもうまくいきます。
update_attributesの記述が多いのは何故なんでしょうか・・・。
update_attributesだと、値を複数同時に更新できるとのことですが、
今回はcheckedだけだし・・?
<h2>モデルファイル記述②</h2>
```perl:post.rb
def create_notification_by(current_user)
notification = current_user.active_notifications.new(
post_id: id,
visited_id: user_id,
action: 'post_comment'
)
if notification.visitor_id == notification.visited_id
notification.checked = true
end
notification.save if notification.valid?
end
end
notification.checked = true
end```の部分は最後に付け加える方が良いと思います。
というのも、自分には通知がこないようにする記述なので、これ入れると、
通知できてるかどうかを、他のユーザーでログインして見ないといけないので面倒です。
自分の記事に対するコメントは、あらかじめchecked true=既読にして、通知がこないようにします。
<h2>Viewファイル</h2>
```perl:notifications/index.html.erb
<div class="container">
<div class='row justify-content-center mx-auto'>
<div class="notification_box">
<h4 class="text-center"><i class="fas fa-bell"></i>通知</h4>
<p class="text-right"><%= link_to "全削除",notification_path(@notifications), method: :delete,class:"btn btn-light fas fa-trash" %></p>
<% if @notifications.exists? %>
<%= render @notifications %>
<% else %>
<p>通知はありません</p>
<% end %>
</div>
</div>
</div>
<% visiter = notification.visiter %>
<% post = notification.post %>
<ul>
<li><%= link_to user_path(visiter) do %><strong><%=visiter.name %><% end %></strong>さんが
<strong><%= link_to post_path(notification.post_id) do %><%= post.title.truncate(20)%><% end %></strong>にコメントしました。
<%= " (#{time_ago_in_words(notification.created_at)} 前)" %></li>
</ul>
time_ago_in_wordsはお決まりのものです。
module NotificationsHelper
def unchecked_notifications
@notifications = current_user.passive_notifications.where(checked: false)
end
end
helperファイルにこれまでコードを書いたことがなく、、
何故ここに?、何を書くのかわかりませんでした・・。
helperって何??
form_withやlink_toもrailsのヘルパーです。 ヘルパーファイルに書くことで、それと同様に、ヘルパーで定義したメソッドを viewで呼び出したりできる。使いこなせると、スッキリした開発につながるようです。 今回で言うと、unchecked_notificationsと書けば、 その中に定義している@notifications =〜が取り出せるんですね!以下の記事がとてもわかりやすいと思いました!
<li class="nav-item">
<% if unchecked_notifications.any? %>
<li><%= link_to "Notice",notifications_path, class: "btn btn-light fas fa-bell",style: "color:tomato;"%>
<% else %>
<li><%= link_to "Notice",notifications_path, class: "btn btn-light fas fa-check"%>
<% end %>
</li>
参考にさせていただいた記事
おわり
いいね については、通知はせずに、
もらったいいね数をポイントとしてマイページに表示させたいと思います!!
QiitaでいうContributionみたいなやつです。