🍿

【Ruby on Rails】【通知機能】コメントへのいいねを通知する

2024/08/01に公開

はじめに

現在Ruby on Railsで、記事投稿アプリを作成しております。
その中でこちらの記事を参考に、通知機能を作成しました。

現在作成が完了している通知内容は、下記となります。

  • 自分の投稿した記事にいいねが付いた時の通知
  • 自分の投稿した記事にコメントが投稿された時の通知
  • 自分のコメントにいいねが付いた時の通知

最初の2つの通知に関しては、先ほどの記事および他の個人記事でも多く実装内容が載っておりましたので、ここでの実装は割愛させていただきます。

現在作成しているアプリではコメントへのいいねボタンを実装済であり、自分の投稿したコメントへいいねが付いた場合にも通知されるようにしたかったため作成しました。
また他の通知機能も実装予定になるため、実装完了次第記事にしたいと思っています。

環境

Rails 7.1.3.4

前提

  • 投稿機能実装済み(この記事ではArticleモデル)
  • コメント機能実装済み(この記事ではCommentモデル)
  • コメントへのいいね機能実装済み(この記事ではCommentfavoriteモデル)
  • こちらの記事を参考に、「自分の投稿した記事にいいねが付いた時の通知」「自分の投稿した記事にコメントが投稿された時の通知」を実装済み
  • 上記2つ実装済のため、通知モデルの作成、通知モデルの関連付は設定済み

実装方法

ここからは、コメントへのいいねを通知する実装内容のみ、記載させていただきます。
こちらの記事内容が前提になっておりますので、説明を割愛している箇所に関してはこちらの記事をご確認ください。

通知モデルの作成、モデルの関連付け

上記にも記載している通り、すでにモデル作成、それぞれのテーブルの関連づけは完了しているため割愛させていただきます。
記事内容と異なるところは、visitor_id、visited_id、article_id、comment_id、actionへのnull制約はつけていない点です。
理由は、今後実装予定の通知機能で上記idがnullになる可能性があるためです。

通知作成メソッドを作る

「コメントへのいいね」が押されたタイミングで、下記データを作成します。
当初実装した際、article_idは不要と考えておりましたが私のアプリではarticleが親、commentが子となるようにルーティングを設定しているため、article_idも必要になります。

  • config/routes.rb
resources :articles do
    resources :favorites, only: %i[create destroy]
    resources :comments, shallow: true do
      resources :commentfavorites, only: %i[create destroy]
    end
  end
  • コメントへのいいね通知に必要なid
visitor_id visited_id article_id comment_id action checked
いいねした人のid いいねされた人のid いいねされたコメントが載っている記事のid いいねされたコメントのid commentlike false

ここまで確認できたところで、実際にcommentモデルにメソッドを作成していきます。

  • app/models/comment.rb
def create_notification_commentlike!(current_user)
    # すでにコメントに、いいねされているかの確認
    temp = Notification.where(['visitor_id = ? and visited_id = ? and comment_id = ? and article_id = ? and action = ?', current_user.id,
                               user_id, id, article_id, 'commentlike'])

    # 上記で確認したtempで、いいねされていない場合のみ通知レコードを作成
    return if temp.present?

    # 現在のユーザーで、相手に送る通知を作成する
    notification = current_user.active_notifications.new(
      visited_id: user_id,
      comment_id: id,
      article_id:,
      action: 'commentlike'
    )

    # 自分のコメントに対するいいねは、通知済みにする
    notification.checked = true if notification.visitor_id == notification.visited_id
    notification.save if notification.valid?
  end
end

すでにコメントにいいねが押されている場合、自分のコメントに自分でいいねした場合の通知は除外します。
基本的には記事へのいいね通知と同じメソッドですが、articleが親、commentが子となるようにルーティングを設定しているため、コメントがどの記事に属しているかを記録するためにarticle_idも必要という点がポイントになるかと思います。article_idもメソッドに追加することで、通知を適切に管理できました。

通知作成メソッドの呼び出し

自分が投稿したコメントへいいねが押されたタイミングで、通知レコードを作成します。
先ほど追加したメソッドを、commentfavoritesコントローラーへ追加します。

  • app/controllers/commentfavorites_controller.rb
    コメントいいねの通知、と補足している箇所が通知作成メソッドの呼び出し箇所になります。
class CommentfavoritesController < ApplicationController
  def create
    @comment = Comment.find(params[:comment_id])
    @commentfavorite = current_user.commentfavorites.new(comment_id: params[:comment_id])
    @commentfavorite.save

    # コメントいいねの通知
    @comment.create_notification_commentlike!(current_user)
       # ここまで
  end

  def destroy
    @commentfavorite = Commentfavorite.find(params[:id])
    @comment = @commentfavorite.comment
    @commentfavorite.destroy
  end
end

通知一覧画面の作成

こちらも作成済み、かつ基本的に参考にさせていただいた内容と同じ実装なため割愛いたします。
コメントした内容へのいいねが押された時のviewは、下記となります。

app/views/notifications/_notification.html.erb

<% visitor = notification.visitor %>
<% visited = notification.visited %>
<%= visitor.name %>さんが

  <% case notification.action %>
    <% when 'commentlike' then %>
      <%= link_to 'あなたのコメント', notification.comment.article, style: 'font-weight: bold;' %>にいいねしました。
      <%= time_ago_in_words(notification.created_at).upcase %>
  <% end %>

コメントへいいねが付いた場合の遷移先はnotification.comment.articleとなります。
各モデルのアソシエーションの関連性への理解が浅く、このコードにたどり着くまでエラーに遭遇したのでその内容も下記へ記載します。

直面したエラー
①No view templateエラー
Image from Gyazo

先ほども書いた通り、当初の実装では通知作成メソッドにarticle_idを入れておりませんでした。
また、_notification.html.erbではnotification.commentとしていたため、
No view templateエラーに遭遇しました。
今回のアプリ構成では、記事詳細画面上にコメント・コメントへのいいねができるようになっているため、comment_id、article_idの両方が必要と気づくことができました。


②NoMethodエラー
通知作成メソッドにarticle_idを追加した後、_notification.html.erbでnotification.article.commentとコードを修正しましたが、こちらでは下記エラーが発生しました。
Image from Gyazo

上記エラーの理由は、それぞれのモデルの関連付けが下記となっているためです。(今回の説明で必要なアソシエーションのみ記載してます)

# Notificationモデル
class Notification < ApplicationRecord
  belongs_to :article, optional: true
  belongs_to :comment, optional: true
end

# Commentモデル
class Comment < ApplicationRecord
  belongs_to :article
  has_many :notifications, dependent: :destroy
end

# Articleモデル
class Article < ApplicationRecord
  has_many :comments, dependent: :destroy
  has_many :notifications, dependent: :destroy
end

正しいコードは、notification.comment.articleです。
notification.comment は Comment モデルのインスタンスで、Comment モデルには belongs_to :article の関連があるため、notification.comment.article でコメントが関連付けられている記事にアクセスすることができました。

今回のエラー発生時には、notification.article.commentと実装していました。
notification.article は Article モデルのインスタンスですが、
Article モデルにはcommentというメソッドは存在しません。
Article モデルは has_many :commentsを持っていて、これにより article.comments でコメントを取得できますが、article.comment というメソッドは定義されていないため、NoMethodエラーが発生していました。

最初はそれぞれのアソシエーションの関連性を十分に考慮できていなかったため、エラーに遭遇していました。
なぜNoMethodエラーが発生しているのかを考えた結果、このコードに辿り着くことができました。

最後に

参考にさせていただいた記事内容をもとに、コメントへのいいね通知を自力で実装でき良かったです。
他の通知機能も、実装できるよう頑張りたいと思います。
ありがとうございました。

Discussion