目次
完成のイメージ
通知の一覧画面と通知があった場合は、各ユーザーの画面上に【新着】マークを付けておくようにします。
前提
- 投稿機能を実装済み(この記事では、Informationモデル)
- ログイン・ユーザー登録機能を実装済み(この記事では、Userモデル)
参考にした記事
今回は、以下のサイトを参考に、「お知らせを投稿したら、登録済みユーザー全てに通知が送られる」機能を実装したいと思います。
【Rails】通知機能を誰でも実装できるように解説する【いいね、コメント、フォロー】
これができると、色々な機能に通知を持たせることができます。
通知モデルの概要
今回のテーブルの情報としては、以下のようになります。
visitor_id | visited_id | information_id | action | checked |
---|---|---|---|---|
1 | 2 | 3 | information | false |
1 | 1 | 3 | information | false |
- visitor_id : 通知を送ったユーザーのid
- visited_id : 通知を送られたユーザーのid
- information_id : 投稿のid
- action : 通知の種類
- checked : 通知を送られたユーザーが通知を確認したかどうか真偽値
必要な情報以外は、nil
を格納するようにしても問題ありません。
通知モデルの作成
まずは、コマンドから通知モデルを作成します。
% rails g model Notification
出来上がったマイグレーションファイルを開き、以下のように変更します。
class CreateNotifications < ActiveRecord::Migration[6.1]
def change
create_table :notifications do |t|
t.integer :visitor_id, null: false
t.integer :visited_id, null: false
t.integer :information_id
t.string :action, default: '', null: false
t.boolean :checked, default: false, null: false
t.timestamps
end
add_index :notifications, :visitor_id
add_index :notifications, :visited_id
add_index :notifications, :information_id
end
end
idのところには、検索パフォーマンスを考えてインデックスを張っています。
また、必ず値が設定される列には、nil
を設定できないよう制約を追加します。
通知を確認したかどうかの初期値は、false
(通知未確認)にしておきましょう。
マイグレーションをして、DBにテーブルを作成します。
% rails db:migrate
通知テーブルの完成。
モデル関連付け
User => Notifications
app/models/user.rb
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
- active_notifications:自分からの通知
- passive_notifications:相手からの通知
紐付ける名前とクラス名が異なるため、明示的にクラス名とIDを指定して紐付けます。
また、ユーザーを削除したとき、同時に通知も削除したいので、 dependent: :destroy
を追加します。
information => Notifications
app/models/information.rb
has_many :notifications, dependent: :destroy
今度は、紐付ける名前とクラス名が一致しているため、明示的に指定する必要はありません。
Notifications => User, information
app/models/notification.rb
default_scope -> { order(created_at: :desc) }
belongs_to :information, 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
default_scope
では、デフォルトの並び順を「作成日時の降順」で指定しています。
つまり、常に新しい通知からデータを取得することができるということです。
たとえば、Notification.first
を実行すると、一番古い通知ではなく、一番新しい通知が取得できます。
information
についているoptional: true
は、nil
を許可するものです。
belongs_to
で紐付ける場合はnil
が許可されないのですが、今回はnil
を設定したいので、付けています。
お知らせ通知作成のメソッドを作る
app/models/information.rb
def create_notification_information!(current_user)
# 全ユーザーを取得
temp_ids = User.all.select(:id).distinct
temp_ids.each do |temp_id|
save_notification_information!(current_user, temp_id['id'])
end
end
def save_notification_information!(current_user, visited_id)
notification = current_user.active_notifications.new(
visited_id: visited_id,
information_id: id,
action: 'information'
)
notification.save if notification.valid?
end
お知らせ通知作成メソッドの呼び出し
お知らせを作成したときにメソッドを呼び出します。
def create
@information = Information.new(information_params)
if @information.save
#ここから追加
# 通知アクション
@information.create_notification_information!(current_user)
#ここまで
flash[:success] = 'お知らせを投稿しました。'
redirect_to information_index_path
else
flash.now[:danger] = 'お知らせの投稿に失敗しました。'
render :new
end
end
通知一覧画面の作成
あとは、通知一覧の画面を作っていきましょう。
まずは、通知コントローラーの作成からです。
% rails g controller notifications
通知一覧の画面はindex
で作るので、ルーティングを追加しておきましょう。
他のメソッドは不要なので、only
を追加しています。
config/routes.rb
resources :notifications, only: :index
app/controllers/notifications_controller.rb
class NotificationsController < ApplicationController
def index
@notifications = current_user.passive_notifications
@notifications.where(checked: false).each do |notification|
notification.update(checked: true)
end
end
end
app/views/notifications/index.html.erb
<h1>通知一覧</h1>
<div class="common-contents">
<div class="informations">
<% if @notifications.exists? %>
<ul class="allnotifications-list">
<%= render @notifications %>
</ul>
<% else %>
<p class="allnotifications-no">通知はありません</p>
<% end %>
</div>
</div>
app/views/notifications/_notification.html.erb
<% visitor = notification.visitor %>
<% visited = notification.visited %>
<li>
<% case notification.action %>
<% when 'information' then %>
<h2>【運営より】<%= notification.information.title %></h2>
<p><%= safe_join(notification.information.content.split("\n"), tag(:br)) %></p>
<% end %>
<p class="n-time"><%= time_ago_in_words(notification.created_at).upcase %>前(<%= notification.set_created %>)</p>
</li>
これで、お知らせ通知機能の実装完了です。
お疲れ様でした。
完成系のテーブル確認
お知らせ投稿後に通知のテーブルを確認してみましょう。
+----+------------+------------+----------------+-------------+---------+---------------------------+---------------------------+
| id | visitor_id | visited_id | information_id | action | checked | created_at | updated_at |
+----+------------+------------+----------------+-------------+---------+---------------------------+---------------------------+
| 12 | 2 | 4 | 31 | information | false | 2021-07-15 00:31:33 +0900 | 2021-07-15 00:31:33 +0900 |
| 11 | 2 | 2 | 31 | information | true | 2021-07-15 00:31:33 +0900 | 2021-07-15 00:31:36 +0900 |
| 10 | 2 | 1 | 31 | information | false | 2021-07-15 00:31:33 +0900 | 2021-07-15 00:31:33 +0900 |
| 9 | 2 | 4 | 30 | information | false | 2021-07-14 23:08:58 +0900 | 2021-07-14 23:08:58 +0900 |
| 8 | 2 | 2 | 30 | information | true | 2021-07-14 23:08:58 +0900 | 2021-07-14 23:09:00 +0900 |
| 7 | 2 | 1 | 30 | information | true | 2021-07-14 23:08:58 +0900 | 2021-07-14 23:13:22 +0900 |
1, 2, 4のユーザーしかいませんでしたので、しっかりと全てのユーザーに対して、通知が送られています。
既読か未読かは、checked
でtrue
かfalse
かを確認しましょう。