LoginSignup
0
0

More than 1 year has passed since last update.

【Rails】いいね/コメント/フォロー/DMの通知機能

Posted at

はじめに

通知機能の復習のため残しておきます。

手順

前提

いいね/コメント/フォロー/DM機能はそれぞれ作成済みとします。

今回は、

  • post(投稿)に対するいいね、コメント
  • user同士のフォロー
  • フォローユーザー同士であればdm可能

という条件で機能を作成しています。

やったこと

まず、通知の流れとして、
1.いいね/コメント/フォロー/DMのcreate時に、独自のメソッドで通知を作成
2.独自のメソッドが定義されているモデルへ行き、指定された処理を行う

いいね、コメント、フォロー、DMの順で記します。

いいね通知

favorite_controller.rb
@post = Post.find(params[:post_id]) #いいねをする投稿を探す
favorite = current_user.favorites.new(post_id: @post.id) #いいねを作成
favorite.save
@post.create_notification_favorite!(current_user) #いいねの通知を作成
post.rb
def create_notification_favorite!(current_user)
  favorite_exist = Notification.where("visiter_id = ? and visited_id = ? and post_id = ? and action = ? ", current_user.id, user.id, id, 'favorite') # いいねしているか検索
  if favorite_exist.blank? # いいねしていない場合は通知を作成
    notification = current_user.active_notifications.new(
      post_id: id,
      visited_id: user_id, #通知相手に相手のidを指定
      action: 'favorite', #helperで後ほど使うためわかりやすい名前をつける
      checked: false #通知確認済み=true, 未確認=falseであり、defaultでfalseを設定しておく 
      )
    notification.save if notification.valid?
  end
end

コメント通知

post_comment_controller.rb
def create
    @post = Post.find(params[:post_id])
    @post_comment = PostComment.new(post_comment_params)
    @post_comment.post_id = @post.id
    @post_comment.user_id = current_user.id
    @comment_post = @post_comment.post
    if @post_comment.save
      @comment_post.create_notification_comment!(current_user, @post_comment.id) #コメントの通知を作成
      @post_comment = PostComment.new(post_comment_params)
    end
  end
post.rb
def create_notification_comment!(current_user, comment_id)
    comment_users = PostComment.select(:user_id).where(post_id: id).where.not(user_id: current_user.id).distinct # 自分以外にコメントしている人をすべて取得し、全員に通知を送る
    comment_users.each do |comment_user|
      save_notification_comment!(current_user, comment_id, comment_user['user_id']) if comment_users.blank?
    end
      save_notification_comment!(current_user, comment_id, user_id) if comment_users.blank? # まだ誰もコメントしていない場合は、投稿者に通知を送る

  end

  def save_notification_comment!(current_user, comment_id, visited_id) 
    # 複数回のコメントを考慮。1つの投稿に複数回通知する
    notification = current_user.active_notifications.new(
      post_id: id,
      comment_id: comment_id,
      visited_id: visited_id,
      action: 'comment',
      checked: false
    )
    notification.save! if notification.valid?
  end

フォロー通知

relationships_controller.rb
def create
    @user = User.find(params[:user_id])
    current_user.follow(params[:user_id])
    @user.create_notification_follow!(current_user) #フォロー通知を作成
end
user.rb
def create_notification_follow!(current_user)
    follow_exist = Notification.where(["visiter_id = ? and visited_id = ? and action = ? ", current_user.id, id, 'follow'])
    if follow_exist.blank?
      notification = current_user.active_notifications.new(
        visited_id: id,
        action: 'follow',
        checked: false
        )
      notification.save if notification.valid?
  end
end

DM通知

usersとrooms(チャットルーム)を繋ぐ中間テーブルとして、
chats(メッセージとuserを紐付けて保管)
user_rooms(チャットルームに入るユーザー2名を紐付けて保管)
としています。

今回は、roomsで行うべき処理をchatsで行なっているので少し違和感ですが、とりあえず機能はしています。

chats_controller.rb
    @chat = current_user.chats.new(chat_params)
    @room = @chat.room
    @chat.save
    @chat.room.create_notification_dm!(current_user, @chat.room_id, @chat.id)
    @chats = @room.chats
    @chat = Chat.new(room_id: chat_params[:room_id])
room.rb
def create_notification_dm!(current_user, room_id, chat_id)
    dm_user = UserRoom.select(:user_id).where(room_id: room_id).where.not(user_id: current_user.id).distinct
    dm_user.each do |dm_user|
      save_notification_dm!(current_user, chat_id, dm_user['user_id'], room_id) 
    end
    # 初めてメッセージを送った場合
    if dm_user.blank?
      visited_id = UserRoom.where(room_id: room_id).where.not(user_id: current_user.id).distinct.first.user_id
      save_notification_dm!(current_user, chat_id, visited_id, room_id)
    end
  end
  def save_notification_dm!(current_user, chat_id, visited_id, room_id)
    # 複数回のコメントを考慮。1つの投稿に複数回通知する
    notification = current_user.active_notifications.new(
      room_id: room_id,
      chat_id: chat_id,
      visited_id: visited_id,
      action: 'dm',
      checked: false
    )
    notification.save! if notification.valid?
  end

通知一覧を表示

notifications_helper.rb
def notification_form(notification)
      @visiter = notification.visiter
      @comment = nil
      @chat = nil
      #notification.actionがfollowか、favoriteか、commentか、dmか
      case notification.action
        when "follow" then
          tag.a(notification.visiter.nickname, href:user_path(@visiter), style:"font-weight: bold;")+"があなたをフォローしました"
        when "favorite" then
          tag.a(notification.visiter.nickname, href:user_path(@visiter), style:"font-weight: bold;")+"が"+tag.a('あなたの投稿', href:post_path(notification.post_id), style:"font-weight: bold;")+"にいいねしました"
        when "comment" then
          @comment = PostComment.find_by(id: notification.comment_id)&.comment
          tag.a(@visiter.nickname, href:user_path(@visiter), style:"font-weight: bold;")+"が"+tag.a('あなたの投稿', href:post_path(notification.post_id), style:"font-weight: bold;")+"にコメントしました"
        when "dm" then
          @chat = Chat.find_by(id: notification.chat_id)&.message
          tag.a(@visiter.nickname, href:user_path(@visiter), style:"font-weight: bold;")+"があなたに"+tag.a('メッセージ',href:chat_path(notification.visiter), style:"font-weight: bold;")+"を送りました"
      end
    end
    def unchecked_notifications
      @notifications = current_user.passive_notifications.where(checked: false)
    end
notifications_controller.rb
  def index
    @notifications = Notification.where(visited_id: current_user.id, checked: false).where.not(visiter_id: current_user.id).to_a
      # 通知の確認済みをtrueに変更して保存したいため、.to_aで配列とし、ビューでは表示可能に。処理としてはchecked: trueにするところまで行っており、リロードすると確認ずみとなる。
    @notifications.each do |notification|
      notification.update_attributes(checked: true)
      @comment = PostComment.find_by(id: notification.comment_id)
    end
  end

  def destroy
    notification = Notification.find(params[:id])
    notification.destroy
    redirect_back(fallback_location: root_path)
  end

  def destroy_all
    @checked_notifications = Notification.where(visited_id: current_user.id, checked: true).where.not(visiter_id: current_user.id)
    @checked_notifications.destroy_all
    redirect_back(fallback_location: root_path)
  end
notifications/index.html.erb
<div id="tab-contents">
  <div id="tab1" class="tab notifications-index">
    <% if @notifications.present? %>
      <table class="table table-borderless">
        <% @notifications.each do |notification| %> #@notifications(未確認の通知たち)があればeachで表示
          <tr>
            <td>
              <%= link_to user_path(notification.visiter) do %>
                <%= attachment_image_tag notification.visiter, :profile_image, :fill,65,65, format: "jpeg", fallback: "no_image.png", size: "65x65", class: "rounded-circle" %>
              <% end %>
            </td>
            <td>
              <p><%= notification_form(notification) %><span class="moderate-font"><%= " (#{time_ago_in_words(notification.created_at)} 前)" %></span></p>
            </td>
          </tr>
        <% end %>
      </table>
    <% else %>
      <p>未読の通知はありません</p>
    <% end %>
  </div>
</div>

お好みで、@notificationsの中身を既読済みに変えてアレンジすると使いやすくなるかと。

終わりに

記述量は多いですが、一つひとつ理解することで応用できるように。
とはいえまだ疑問点は残るため、解消できるよう頑張るのみ。

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