0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【Rails】通知機能を実装する

Posted at

1. 概要

ゴール

  • 自分の投稿に コメント / いいね が付いたら通知レコードを作成
  • 通知は /notifications に一覧表示(自分の分だけ)
  • 通知は 既読化 できる
  • 投稿機能、ログイン機能、いいね機能、コメント機能を実装済み
    • いいね機能またはコメント機能どちらか一方しか実装していない場合、以降の comment/like どちらか一方だけ実装してください

追加・編集するファイル

  • Migrationdb/migrate/*_create_notifications.rb
  • Modelapp/models/notification.rb(新規)
    user.rb / post.rb / comment.rb / like.rb に関連追記
  • Controller
    • 通知ページ:app/controllers/notifications_controller.rb(新規)
    • 通知作成(追記):comments_controller.rb / likes_controller.rb
  • Viewapp/views/notifications/index.html.erb(新規)
  • Routesconfig/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 %>
0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?