1. 概要
ゴール
- 自分の投稿に コメント / いいね が付いたら通知レコードを作成
- 通知は
/notificationsに一覧表示(自分の分だけ) - 通知は 既読化 できる
- 投稿機能、ログイン機能、いいね機能、コメント機能を実装済み
- いいね機能またはコメント機能どちらか一方しか実装していない場合、以降の
comment/likeどちらか一方だけ実装してください
- いいね機能またはコメント機能どちらか一方しか実装していない場合、以降の
追加・編集するファイル
-
Migration:
db/migrate/*_create_notifications.rb -
Model:
app/models/notification.rb(新規)
+user.rb/post.rb/comment.rb/like.rbに関連追記 -
Controller:
- 通知ページ:
app/controllers/notifications_controller.rb(新規) - 通知作成(追記):
comments_controller.rb/likes_controller.rb
- 通知ページ:
-
View:
app/views/notifications/index.html.erb(新規) -
Routes:
config/routes.rb
2. マイグレーション
コマンドプロンプト
rails g model Notification user:references post:references comment:references like:references action:string checked:boolean
db/migrate/XXXXXX_create_notifications.rb を編集
db/migrate/XXXXXX_create_notifications.rb
class CreateNotifications < ActiveRecord::Migration[7.0]
def change
create_table :notifications do |t|
t.references :user, null: false, foreign_key: true # 通知の受け手(投稿者)
t.references :post, null: false, foreign_key: true # 対象の投稿
t.references :comment, null: true, foreign_key: true # コメント通知のときだけ入る
t.references :like, null: true, foreign_key: true # いいね通知のときだけ入る
t.string :action, null: false # "comment" or "like"
t.boolean :checked, null: false, default: false # 既読フラグ
t.timestamps
end
end
end
※ 編集してから rails db:migrate を行ってください!
コマンドプロンプト
bin/rails db:migrate
3. モデル
app/models/notification.rb
class Notification < ApplicationRecord
belongs_to :user
belongs_to :post
belongs_to :comment, optional: true
belongs_to :like, optional: true
scope :unchecked, -> { where(checked: false) }
end
app/models/user.rb
class User < ApplicationRecord
has_many :notifications, dependent: :destroy
end
app/models/post.rb
class Post < ApplicationRecord
belongs_to :user
has_many :comments, dependent: :destroy
has_many :likes, dependent: :destroy
has_many :notifications, dependent: :destroy
end
app/models/comment.rb
class Comment < ApplicationRecord
belongs_to :user
belongs_to :post
has_many :notifications, dependent: :destroy
end
app/models/like.rb
class Like < ApplicationRecord
belongs_to :user
belongs_to :post
has_many :notifications, dependent: :destroy
end
4. ルーティング
コマンドプロンプト
resources :notifications, only: [:index, :update]
5. コントローラー
notifications_controller.rb
app/controllers/notifications_controller.rb
class NotificationsController < ApplicationController
before_action :authenticate_user!
def index
@notifications = current_user.notifications.order(created_at: :desc)
end
def update
n = current_user.notifications.find(params[:id])
n.update!(checked: true)
redirect_to post_path(n.post)
end
end
comments_controller.rb
app/controllers/comments_controller.rb
class CommentsController < ApplicationController
before_action :authenticate_user!
def create
post = Post.find(params[:post_id])
comment = post.comments.build(comment_params.merge(user: current_user))
comment.user_id = current_user.id
if comment.save
create_comment_notification(post, comment) # 追加
flash[:success] = "コメントしました"
redirect_back(fallback_location: root_path) #直前のページにリダイレクト
else
flash[:danger] = "コメントに失敗しました"
redirect_back(fallback_location: root_path) #直前のページにリダイレクト
end
redirect_back(fallback_location: root_path)
end
# ======== ここから ========
private
def comment_params
params.require(:comment).permit(:content)
end
def create_comment_notification(post, comment)
return if post.user_id == current_user.id # 自分には通知しない
Notification.create!(
user: post.user,
post: post,
comment: comment,
action: "comment"
)
end
# ======== ここまで ========
end
LikesController
app/controllers/likes_controller.rb
class LikesController < ApplicationController
before_action :authenticate_user! # 追加
before_action :set_post # 追加
def create
like = current_user.likes.create!(post_id: params[:post_id])
create_like_notification(@post, like) # 追加
redirect_back(fallback_location: root_path)
end
def destroy
like = Like.find_by(post_id: params[:post_id], user_id: current_user.id)
like.destroy
redirect_back(fallback_location: root_path)
end
# ======== ここから ========
private
def set_post
@post = Post.find(params[:post_id])
end
def create_like_notification(post, like)
return if post.user_id == current_user.id
Notification.create!(
user: post.user,
post: post,
like: like,
action: "like"
)
end
# ======== ここまで ========
end
6. ビュー
app/views/notifications/index.html.erb
<h1>通知</h1>
<p><%= link_to "投稿一覧へ", posts_path %></p>
<% if @notifications.any? %>
<ul>
<% @notifications.each do |n| %>
<li style="<%= 'font-weight:bold;' unless n.checked %>">
<% if n.action == "comment" && n.comment.present? %>
<%= link_to(n.post.title.presence || "投稿", post_path(n.post)) %> に
<%= n.comment.user.email %> さんがコメント:
「<%= truncate(n.comment.content, length: 30) %>」
<% elsif n.action == "like" && n.like.present? %>
<%= link_to(n.post.title.presence || "投稿", post_path(n.post)) %> に
<%= n.like.user.email %> さんが「いいね!」
<% end %>
<small> / <%= time_ago_in_words(n.created_at) %>前</small>
<% unless n.checked %>
<%= button_to "既読にする", notification_path(n), method: :patch %>
<% end %>
</li>
<% end %>
</ul>
<% else %>
<p>通知はまだありません。</p>
<% end %>
通知ページに飛ぶ用のリンク:
<%= link_to "通知", notifications_path %>