はじめに
この記事はプログラミング初学者が他の記事を参考にしたり、実際に実装してみたりして、アウトプットの一環としてまとめたものです。内容に不備などあればご指摘いただますと幸いです。
今回Xクローン作成中にコメント機能を実装しました。
そのコメント機能について備忘録も兼ねて記事を作成していきます。
実現したいこと
投稿されたツイートに対して、コメントをつけることができる機能を実装すること。
コメント機能について
コメントする時のイメージ

- ①のユーザーに注目すると、1人のユーザーから複数の線が伸びています。
→ つまりユーザーは「たくさんのツイートにコメント」することができます。 - ②のツイートに注目すると、1つのツイートから複数の線が伸びています。
→つまりツイートは「たくさんのユーザーにコメント」されます。
このように 「ユーザーもツイートもたくさん持っている」関係を多対多(M:N)の関係 といいます。
そして、多対多(M:N)の関係には中間テーブルが必要!
中間テーブルを用いてER図を表すと

このようになります。
Commentモデルとテーブルの作成
それでは、実装していきましょう!
以下のコマンドでモデルを作成します。
$ rails g model comment user_id:integer tweet_id:integer content:text
マイグレーションファイルは以下のように生成されます。
class CreateComments < ActiveRecord::Migration[7.0]
def change
create_table :comments do |t|
t.integer :user_id
t.integer :tweet_id
t.text :content
t.timestamps
end
end
end
以下のコマンドでマイグレーションを実行しましょう。
$ rails db:migrate
アソシエーションの設定
それぞれのファイルに以下を追加します。
belongs_to :user
belongs_to :tweet
has_many :comments, dependent: :destroy
has_many :comments, dependent: :destroy
dependent: :destroy
は、has_many
で使えるオプションです。
1:Nの関係において、「1」のデータが削除された場合、関連する「N」のデータも削除される設定。
ここでは、ツイートが削除された場合に関連するコメントも削除されるように設定してます。
ルーティングの設定
コメントは、ツイートに対して、コメントするのでネストしたルーティングを作成します。
resources :tweets do
resources :comments, only: [:create]
end
こんな感じで作成されます。
また、ネストしたルーティングを作成したことでtweet_id
はparams[:tweet_id]
で取得できます。
Controllerの作成
以下のコマンドでコントローラを作成します。
$ rails g controller comments
生成されたコントローラのファイルを以下のように編集します。
class CommentsController < ApplicationController
def create
@tweet = Tweet.find(params[:tweet_id])
# @tweetに関連したCommentクラスの新しいインスタンスを作成。
# つまり、comment.tweet_id = @tweet.idが済んだ状態で生成されている。
# buildはnewと同じ意味で、アソシエーションしながらインスタンスをnewする時に形式的に使われる。
@comment = @tweet.comments.build(comment_params)
# コメントの所有者を現在ログインしているユーザーに設定。
@comment.user_id = current_user.id
@comment.save
# 直前のページにリダイレクトする。
redirect_to request.referer, notice: 'コメントをしました。'
end
private
def comment_params
params.require(:comment).permit(:content, images: [])
end
end
コメントを投稿するためのインスタンス変数を定義
def show
@tweet = Tweet.find(params[:id])
@comments = @tweet.comments.order(created_at: 'DESC')
@comment = current_user.comments.new # 追加
end
Viewの作成
ツイートの詳細画面でコメントを投稿できるようにします。
= form_with model: [@tweet, @comment], url: tweet_comments_path(@tweet) do |f|
= f.text_area :content, class: "post-content", placeholder: "返信をポスト" # テキスト投稿フォームを作成。
div.d-flex.justify-content-between.pt-2.pb-2
ul.post-icon-list.d-flex
li.ms-2
div
= f.label :images
i.bi.bi-card-image style="cursor: pointer"
= f.file_field :images, multiple: true, style: "display: none" #投稿画像のファイル選択ボックスを作成。Bootstrapを用いてfile_fieldの見た目をアイコンにしてます。
li
div
i.bi.bi-filetype-gif
li
div
i.bi.bi-emoji-smile
li
div
i.bi.bi-geo-alt
div
= f.submit "返信", class: "post-btn" # 送信ボタンを作成。
form_withを記述する際、ルーティングでネストを定義している時は[@post, @comment]
のように、配列で二つ ([関連元のインスタンス, 関連先のインスタンス]) 渡す必要があります。
おわりに
最後まで読んでいただきありがとうございました。
少しでも皆さんの参考になれば幸いです。
参考にしたサイト