0
1

More than 1 year has passed since last update.

[Rails] コメント機能実装

Last updated at Posted at 2023-02-04

はじめに

コメント機能を実装するための忘備録です。
Userモデル、Articleモデル(Postモデルと同じ)は作成済みで行っています。

実行環境

  • Rails 7.0.4.1
  • Ruby 3.0.4
  • Devise 4.8.1

実装

1. アソシエーション

名称未設定.png
articlesテーブルとcommentsテーブルを1対多でつなぎ、comment_contentのレコードを追加していくだけでコメントを残すことは出来そうですが、
今回は、誰がコメントを追加したかまで実装したいため、articlesテーブルだけでなく、usersテーブルもcommentsテーブルにつなげます。

この場合、usersテーブルとariticlesテーブルは多対多の関係になっており、commentsテーブルはそれを実現させる為の関係テーブルです。

まとめると、
 ・usersとcommentsは1対多の関係
 ・articlesとcommentsは1対多の関係
 ・usersとariticlesは多対多の関係 
になっています。

2.モデルの作成

$ rails g model Comment comment_content:text user:references post:references

データベースへ反映。

$ rails db:migrate

3.マイグレーション

Userモデル:

app/models/user.rb
class User < ApplicationRecord
  # Include default devise modules. Others available are:
  # :confirmable, :lockable, :timeoutable, :trackable and :omniauthable
  devise :database_authenticatable, :registerable,
         :recoverable, :rememberable, :validatable

  has_many :articles, dependent: :destroy
  has_many :comments, dependent: :destroy  #userの削除と同時に、commentも削除される
end

Articleモデル:

app/models/article.rb
class Article < ApplicationRecord
  belongs_to :user
  has_many :comments, dependent: :destroy  #articleの削除と同時に、commentも削除される
end

Commentモデル:

app/models/comment.rb
class Comment < ApplicationRecord
  belongs_to :user
  belongs_to :article
  validates :user_id,    presence: true
  validates :article_id, presence: true
  validates :comment_content, presence: true  #空のコメントを送信できないようにする
end

4.コントローラの作成

コントローラなので複数系を使って作成します。頭文字は大文字でも小文字でも平気なようです。

$ rails g controller posts index show
$ rails g controller comments

articlesコントローラ:

app/controllers/articles_controller.rb
class ArticlesController < ApplicationController
  before_action :authenticate_user!, only: %i[show create destroy]
  before_action :correct_user, only: :destroy

  def index
    @articles = current_user.articles.all
    @article = current_user.articles.new
  end

  def show
    @article = Article.find(params[:id])
    @comment = Comments.new
    @comments = @article.comments
  end
  .
  .
end

今回は、コメント機能の作成のため記事の作成方法は省略します。
viewでarticleのデータとコメントのデータを使いたいので、インスタンス変数で宣言します。(@記号で始まる変数)

commentsコントローラ:

app/controllers/comments_controller.rb
class CommentsController < ApplicationController
  before_action :authenticate_user!
  before_action :correct_user, only: :destroy

  def create
    @comment = current_user.comments.create(comment_params)
    redirect_to request.referer || root_url
  end

  def destroy
    @comment.destroy
    redirect_to request.referer || root_url, status: :see_other
  end

  private

  def comment_params
    params.require(:comment).permit(:comment_content, :article_id)
  end

  def correct_user
    @comment = current_user.comments.find_by(id: params[:id], article_id: params[:article_id])
    redirect_to root_url, status: :see_other if @comment.nil?
  end
end

Deviseのcurrent_userで、今ログインしているユーザーを使えます。
コメントの削除をする際には、viewでも設定するのですが、ログインしているユーザーがしたコメントと、削除するコメントのユーザーが同じか確認できるようにしています。
また、rails7 では、destroyアクションの後に、status: :see_other 付ける必要があります。

5.ルーティングの設定

config/routes.rb
resources :articles do
  resources :comments, only:[:create, :destroy]
end

do..endで、ルーティングを行うと親子関係が作成できます。

$ rails routes を実行すると、

article_comments POST   /articles/:article_id/comments(.:format)       comments#create
article_comment  DELETE /articles/:article_id/comments/:id(.:format)   comments#destroy

/postsの中にcommetnsがあることが確認できます。この/postsの中にcommetnsがあることが確認できます。この関係をネストと言そうです。
DELETEのルーティングをみると、リクエストを送信するのに articleのid, commentのid それぞれが必要になることが分かります。

6.viewの作成

views/articles/show.erb
.
.
    <div class="comments">
      <h2>コメントをする</h2>
        <%=  form_with(model:[@article, @comment], local: true) do |f| %>
          <%= f.text_area :comment_content %>
          <%= f.hidden_field :article_id, value: @article.id %>
          <%= f.submit 'コメントする' %>
        <% end %>
    
      <h2>コメント一覧</h2>
        <span>
        <% @comments.each do |comment| %>
        <div class="comments-index">
          <%= comment.user.name %>
          <%= comment.comment_content %><br>
          Commented <%= time_ago_in_words(comment.created_at) %>前
          <% if current_user === comment.user %>  # ログインしているユーザーと、コメントがそのユーザーのものかの確認
          <div class="comment-delete">
            <%= link_to "delete", article_comment_path(comment.article, comment), data: { "turbo-method": :delete,
                                                      turbo_confirm: "You sure?" } %>
          </div>
        </div>
          <% end %>
        <% end %>
        </span>
    </div>
.
.

articlesコントローラでshowの中に、@article, @comments, @commentを宣言しているので、viewで使うことができます。

form_with(model:[@article, @comment], method: :post) となっていますが、
先程のネストされたルーティングへ、リクエストを送る際には、articleとcomment両方の情報が必要になるからです。

form_withについて:

So when passing such a model record, Rails infers the URL and method.

<%= form_with model: @post do |form| %>
...
<% end %>

is then equivalent to something like:

<%= form_with scope: :post, url: post_path(@post), method: :patch do |form| %>
 ...
<% end %>

https://api.rubyonrails.org/v7.0/classes/ActionView/Helpers/FormHelper.html#method-i-form_with:~:text=So%20when%20passing%20such%20a%20model%20record,patch%20do%20%7Cform%7C%20%25%3E%0A%20%20...%0A%3C%25%20end%20%25%3E

参考にさせていただいた記事

0
1
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
1