やったこと
Railsでアプリを作る課題を行っています。通知機能を作りましょうというタスクがありました。
初めて作る&覚えておくと応用が効きそうなので、いかにやり方をまとめます。
なお、実行環境は以下の通りです。
- Rails
5.2.3
- Ruby
2.6.4
仕様
以下のような仕様になっています。
-
user
がフォローされた時 -
user
の投稿したpost
にコメントがついた時 -
user
の投稿に「いいね!」がされた時
上記の条件でユーザーに通知を出します。
DB設計
こちらのようになっています。
ポリモーフィック関連付けをしようして、activity.subject
の形で、Like
, Comment
, Relationship
のインスタンスが呼び出せるようにしています。
データベース構造の詳細はこちらの記事をご覧ください。
また、activities
テーブルのread
カラムも利用して、既読管理も行いますが、これは、本記事ではなく、次回の記事にて紹介します。
ゴール
上記のテーブル設計から**「activities
のレコードを作成し、現在のuser
のものだけを集約し、それを一覧表示する」**ことが今回のゴールとなりそうです。
通知機能
Model
今回は、
-
user
がフォローされた時 =>relationships
のレコードが作られた時 -
user
の投稿したpost
にコメントがついた時 =>comments
のレコードが作られた時 -
user
の投稿に「いいね!」がされた時 =>likes
のレコードが作られたとき
↑上記3つのアクションが行われたときに、activities
のレコードも同時に作られれば良さそうです。
それは、以下の方法で実装しました。モデルメソッドに書いた内容を、3つまとめて紹介します。
class Like < ApplicationRecord
# 中略
after_create_commit :create_activities
private
def create_activities
Activity.create!(subject: self, user: post.user, action_type: :liked_to_own_post)
end
end
class Relationship < ApplicationRecord
# 中略
after_create_commit :create_activities
private
def create_activities
Activity.create!(subject: self, user: follower, action_type: :followed_me)
end
end
lass Comment < ApplicationRecord
# 中略
after_create_commit :create_activities
private
def create_activities
Activity.create!(subject: self, user: post.user, action_type: :commented_to_own_post)
end
end
create_activities
のメソッドを、Like
, Relationship
, Comment
, のインスタンスが作られた後で呼んでいます。使用しているafter_create_commit
なのですが、after_save
と似ているコールバックです。ただし、データベース変更のコミットが完了するまでトリガされない点が異なるそうです。
詳しい説明はRailsガイドのこちらの記述をどうぞ。
なお、model
にはそれぞれ、ポリモーフィック関連付けを定義する記述もつけています。こちらは、先に挙げたこちらの記事をご覧ください。
今思うと、各モデルのcreate_activities
がプライベートメソッドなのは、別々のモデルに同名のメソッドを使っているからなのですね。。。
(当初は、お手本のメソッドの写経をしていましたが、今、改めて見直してみて思うです。。。)
さて、これで基本的な通知機能は実装できました。まだview
ができていないので、console上で実験すると...
$ rails c
> user = User.first
> user.activities
#=> Activityのインスタンスたちが取得できる
上記のコマンドでuser
に紐づく通知一式が取得できます。これをeach
で回せば、view
で表示できそうです。
追記)Viewで通知を表示する
続けて、Viewを続けて実装します。
仕様は以下の通りです。3種類ある通知をクリックすると、それぞれ以下のページに遷移します。
- 自分がフォローされた時の通知 => フォローしてくれたユーザーの詳細ページ
- 自分の投稿へのコメントの通知 => 該当の投稿
- 自分の投稿へのいいねの通知 => 該当の投稿
それぞれの遷移先は、以下のように設定します。(本当はモデルメソッドとして実装しましたが、記事の範囲内で動作させるために、一時的にヘルパーに記載しています。)
module SomeHelper
def transition_path(activity)
case activity.action_type.to_sym
when :commented_to_own_post
post_path(activity.subject.post, anchor: "js-comment-#{activity.subject.id}")
when :liked_to_own_post
post_path(activity.subject.post)
when :followed_me
user_path(activity.subject.follower)
end
end
end
そして、action_type
(通知のタイプ)と同じ名前のビューを作り...
(ここではサンプルに1例だけ載せます)
= link_to transition_path(activity) do
object
= link_to activity.subject.user.username, user_path(activity.subject.user)
| があなたの
object
=link_to '投稿', post_path(activity.subject.post)
| にいいねしました
.text-right
= l activity.created_at, format: :short
controller
にて、current_userのものだけ集約したactivity
を
class Mypage::ActivitiesController < Mypage::BaseController
def index
@activities = current_user.activities.order(created_at: :desc)
end
end
view
でこんな感じに回すのでした。
- if @activities.present?
- @activities.each do |activity|
= render "#{activity.action_type}", activity: activity
- else
.text-center
| お知らせはありません
render
で呼び出す内容って、動的に生成できるのですね^^
上記の内容で、以下のようにページが表示できました。
以上、簡単ですが、ユーザーへの通知の設定です。ここから、既読管理を作り込んでいきます。