質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

87.59%

メッセージの未読・既読機能が実装出来ない。

解決済

回答 2

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 1,173

score 66

前提・実現したいこと

受信したメッセージの横に、未開封であればマークを表示するといった処理をしたいです。
メッセージ機能及び通知機能は既に実装出来ています。

未読・既読機能としては、以下の流れを考えています。

  1. 通知テーブルにreadと言うboolean型のカラムを追加する。
  2. /rooms/:idにアクセスがあった場合にreadの中身をfalseからtrueに変える処理を施し、未読かどうかを判定する。
  3. trueの場合はマークを表示、/rooms/:idにアクセスがあった瞬間にマークを消す。

ただコードの記述の仕方が分からず足踏みしています。
「ログイン中のユーザーが抱えるmessageに関する通知(全件)を取得する」と言った通知機能は記述出来たのですが、「各room内のmessageに関する通知をそれぞれ取得する」記述の仕方が分かりません…。
ご助言を頂けますと有難いです。

schema.rb

  create_table "notifications", force: :cascade do |t|
    t.integer "visitor_id", null: false
    t.integer "visited_id", null: false
    t.integer "post_id"
    t.string "action", default: "", null: false
    t.boolean "checked", default: false, null: false
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
    t.integer "comment_id"
    t.integer "message_id"
    t.integer "room_id"
    t.boolean "read", default: false, null: false #未読・既読判定の為にカラム追加。
    t.index ["comment_id"], name: "index_notifications_on_comment_id"
    t.index ["message_id"], name: "index_notifications_on_message_id"
    t.index ["post_id"], name: "index_notifications_on_post_id"
    t.index ["room_id"], name: "index_notifications_on_room_id"
    t.index ["visited_id"], name: "index_notifications_on_visited_id"
    t.index ["visitor_id"], name: "index_notifications_on_visitor_id"
  end

messages_controller.rb

  def create    
      @room = @message.room
      if @message.save!
          @roommembernotme = Entry.where(room_id: @room.id).where.not(user_id: @current_user.id)
          @theid = @roommembernotme.find_by(room_id: @room.id)
          notification = @current_user.active_notifications.new(
              room_id: @room.id,
              message_id: @message.id,
              visited_id: @theid.user_id,
              visitor_id: @current_user.id,
              action: 'message'
          )
          if notification.visitor_id == notification.visited_id
              notification.checked = true
              notification.read = true # 自分がメッセージを送った場合は既読にする。
          end
          notification.save! 
      end 

rooms_controller.rb

  def show
      # ログイン中のユーザーが抱える「message」に関する相手からの通知を取得。
      @notifications = @current_user.passive_notifications.where(action: 'message').includes([:visitor], [:visited]).order(created_at: :desc).page(params[:page]).per(7)
      # ログイン中のユーザーが抱える相手からの通知のうち、該当のroom_idを取得し、showを開いた時点でreadをtrueにする。
      @current_user.passive_notifications.find_by(params[:id]).update(read: true)
      # 該当のroom_id内にある未読メッセージの数を取得。
      @unread = @notifications.where(room_id: params[:id], read: false).count
   end

   def index
      @rooms = Room.page(params[:page]).per(3)
      @user = @current_user
      @currentEntries = @current_user.entries
      myRoomIds = []
      @currentEntries.each do | entry |
        myRoomIds << entry.room_id
      end
      @anotherEntries = Entry.includes(:user, :room).where(room_id: myRoomIds).where('user_id != ?', @user.id).order(created_at: :desc)

      @notifications = @current_user.passive_notifications.where(action: 'message').includes([:visitor], [:visited]).order(created_at: :desc).page(params[:page]).per(7)
      @notifications.where(checked: false).each do |notification|
        notification.update_attributes(checked: true)
      end
      @unread = @notifications.where(room_id: params[:id], read: false).count
   end

rooms_index.html.erb

    <div class="message-items">
      <% @anotherEntries.each do |e| %>
        <div class="message-item">
          <div class="message-left">
            <%= link_to room_path(e.room.id) do %>
              <img src="<%= "/user_images/#{e.user.image_name}" %>">
            <% end %>
          </div>
          <div class="message-center">
            <div class="message-center-username">
              <%= link_to room_path(e.room.id) do %
                <%= e.user.name %>
                <%= @unread %>                  
              <% end %>
            </div>
            <div class="message-center-message">
              <%= link_to room_path(e.room.id) do %>
                <% dm = Message.find_by(id: e.room.message_ids.last).try(:content) %>
                <%= truncate(dm, length: 9) %>
              <% end %>
            </div>
          </div>
          <div class="message-right">
            <div class="message-date" style="color: #C0C0C0;"><%= e.updated_at.strftime("%Y/%m/%d") %></div>
            <div class="message-time" style="color: #C0C0C0;"><%= e.updated_at.strftime("%H:%M") %></div>
          </div>
        </div>
      <% end %>
    </div>

補足情報(FW/ツールのバージョンなど)

ruby 2.6.4p104
RubyGems 3.0.3
Rails 5.2.3

【追記】ER図

イメージ説明

【追記】解決したコード

rooms_controller.rb

  def index
    #以下、メッセージ機能に関する記述。変更なし。
    @rooms = Room.page(params[:page]).per(3)
    @user = @current_user
    @currentEntries = @current_user.entries
    myRoomIds = []
    @currentEntries.each do | entry |
      myRoomIds << entry.room_id
    end
    @anotherEntries = Entry.includes(:user, :room).where(room_id: myRoomIds).where('user_id != ?', @user.id).order(created_at: :desc)

    # 既読機能についてはコントローラでは特に記述せず。ビューにて記述。
  end

   def show
    #以下、メッセージ機能に関する記述。変更なし。
    @rooms = Room.page(params[:page]).per(3)
    @user = @current_user
    @currentEntries = @current_user.entries
    myRoomIds = []
    @currentEntries.each do | entry |
      myRoomIds << entry.room_id
    end
    @anotherEntries = Entry.includes(:user, :room).where(room_id: myRoomIds).where('user_id != ?', @user.id).order(created_at: :desc)

    # ここから既読機能。ログイン中のユーザーが抱える「message」に関する「相手からの通知」のうち、該当のroom_idを取得し、showを開いた時点でreadをtrueにする。
    @current_user.passive_notifications.where(action: 'message', read: false, room_id: params[:id]).each do |notification|
      notification.update_attributes(read: true)
    end
   end

rooms_index.html.erb と rooms_show.html.erb(既読機能に関する内容は同じ)

   <!-- ログイン中のユーザーが保持している各Entryの中で、未読メッセージがある場合countを繰り返し表示。-->
   <% message_count = Notification.where(action: "message", visited_id: @current_user.id, visitor_id: e.user.id, room_id: e.room.id, read: false).count %>
   <% if message_count > 0 %>
     <div class="message-count date-and-count"><%= message_count %></div>
   <% end %> 
  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 2

checkベストアンサー

+1

この質問は「追記・修正依頼」で解決しました。

他に書くことが思い当たらないため、rooms_controller.rbをリファクタリングしたものを載せておきます。
動かなかったらすみません。

before_action :set_room, only: [:index, :show]
before_action :set_another_entries, only: [:index, :show]

def index
end

def show
  @current_user
    .passive_notifications
    .where(action: 'message', read: false, room_id: params[:id])
    .update_all(read: true)
end

private

def set_room
  @rooms = Room.page(params[:page]).per(3)
end

def set_another_entries
  my_room_ids = @current_user.entries.pluck(:room_id)
  @another_entries = Entry.includes(:user, :room).where(room_id: my_room_ids).where('user_id != ?', @current_user.id).order(created_at: :desc)
end

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2020/12/20 12:41

    有難うございます!!
    こちらの記述でも、room作成・message送受信・通知機能と全て問題なく作動致しました(質問文には記載していなかった既存の変数はいくつか残す必要あり)。

    配列のくだりはpluckと言うメソッドでこんなにも簡略化出来るのですね…まさに目から鱗が落ちる思いでした。大変勉強になりました。
    親身にご助言も頂きまして有難うございました!

    キャンセル

+1

visitor なのか visited なのかどちらかわかりませんが、それとroom で絞ってください
ああ、visitorの方ですね。そちらは絞れていますから
@notifications.find_by(room_id: params[:id]).update(read: true)
show すべき room が params[:id] で来ている前提です

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2020/12/18 19:54

    nekoさん回答まとめてくださいよ。そちらにベストを

    キャンセル

  • 2020/12/19 19:46

    なるほどです。お二方とも有難うございました!
    winterboumさま、親身にご助言頂いたにも関わらずなかなか解決まで辿り着けず申し訳ありませんでした。
    neko_daisukiさまからご回答があるようであれば、そちらをベストアンサーに選ばさせて頂ければと思います。

    キャンセル

  • 2020/12/19 21:37

    了解しました。
    お二方、お気遣いいただきありがとうございます。

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 87.59%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る