2
9

More than 3 years have passed since last update.

コメント機能を実装

Posted at

コメント機能

今回、コメントはどのツイートに対してのコメントなのか、誰の投稿したコメントなのかが必要になります。そのため、userモデルとtweetモデルがあるとします。まずはその2つとアソシエーションを実装します。
まずはターミナルでモデルを生成します。
【例】モデル名をComentとします。

$ rails g model comment

次にマイグレーションの中身を編集します。

【例】

db/migrate/20XXXXXXXXXXXX_create_comments.rb
class CreateComments < ActiveRecord::Migration[5.2]
  def change
    create_table :comments do |t|
      t.integer :user_id
      t.integer :tweet_id
      t.text :text
      t.timestamps
    end
  end
end

上記のように記述すると、コメントの本文はtextカラムに保存していきます。
コメントはまず誰が投稿したコメントなのか分かる必要があるので、結びつくユーザーのidを保存する必要があります。コメントを投稿したユーザーのidを保存するカラムがuser_idとなります。
コメントはどのツイートに対してのコメントなのか明示する必要があり、結びつくツイートのidを保存するカラムがtweet_idとなります。

次はターミナルで

$ rails db:migrate

を実行します。

アソシエーション

次はアソシエーションを編集していきます。
userは一人対してtweetはいくつも投稿できるので「1対多」です。
userは一人に対してcommentはいくつも投稿できるので「1対多」です。
tweetはひとつのツイートに対してcommentはいくつも投稿できるので「1対多」です。
アソシエーションを定義していきます。

app/models/user.rb
class User < ApplicationRecord
  # Include default devise modules. Others available are:
  # :confirmable, :lockable, :timeoutable and :omniauthable
  devise :database_authenticatable, :registerable,
         :recoverable, :rememberable, :validatable
  has_many :tweets
  has_many :comments
end
app/models/tweet.rb
class Tweet < ApplicationRecordうに
  validates :text, presence: true  #バリテーションしてます
  belongs_to :user
  has_many :comments 
end
app/models/comment.rb
class Comment < ApplicationRecord
  belongs_to :tweet
  belongs_to :user 
end

上記のように記述します。
ちなみにbelongs_toとhas_manyのアソシエーションの定義は
belongs_to :モデル単数形
has_many :モデル複数形
です。

ルーティングのネスト

ネストとはある記述の中に入れ子構造で別の記述をする方法です。
ルーティングでネストを利用すると、「あるコントローラーのルーティング内に、別のコントローラーのルーティングを記述すること」となります。
【例】ルーティングのネスト

Rails.application.routes.draw do
  resources :親となるコントローラー do
    resources :子となるコントローラー
  end
end

今回のは、tweets_controller.rbのルーティングの中にcomments_controller.rbのルーティングを記述します。これにより、コメントに結びつくツイートのidの情報を含んだパスを受け取れるようになります。

config/routes.rb
Rails.application.routes.draw do
  devise_for :users
  root to: 'tweets#index'
  resources :tweets do
    resources :comments, only: :create  #createアクションしか使わないのでonlyにしてます
  end
  resources :users, only: :show  #showアクションしか使わないのでonlyにしてます
end

上記のようにdoとendで挟むことで中の記述をネストさせました。今回はコメント情報を作る機能しか使わないのでcreateアクションのみにしています。

commentsコントローラーを生成

ターミナルで

$ rails g controller comments
app/controllers/comments_controller.rb
class CommentsController < ApplicationController
  def create
    comment = Comment.create(comment_params)
    redirect_to "/tweets/#{comment.tweet.id}"
  end

  private
  def comment_params
    params.require(:comment).permit(:text).merge(user_id: current_user.id, tweet_id: params[:tweet_id])
  end
end

Commentモデルのcreateメソッドの引数では、ストロングパラメーターを用いてカラムを指定しています。
redirect_toの後にはルーティングのURLやPrefixを記述することでそのアクションを実行することができます
tweetsコントローラのshowアクションを実行するにはツイートのidが必要です。
そのため、ストロングパラメーターを用いた上で変数commentに代入します。
textカラムには渡されたparamsの中にcommentというハッシュがある二重構造になっているので、requireメソッドで指定してtextを取り出します。
user_idカラムには、ログインしているユーザーのidとなるcurrent_user.idを保存し、
tweet_idカラムは、paramsで渡されるようにするので、params[:tweet_id]として保存しています。

コメント投稿用フォームのビュー

【例】

app/views/tweets/show.html.erb
〜省略〜
<div class="XXXX">
    <% if current_user %>
      <%= form_with(model: [@tweet, @comment], local: true) do |form| %>
        <%= form.text_area :text, placeholder: "コメントする", rows: "2" %>
        <%= form.submit "送信" %>
      <% end %>
    <% else %>
      <strong><p>※※※ コメントの投稿には新規登録/ログインが必要です ※※※</p></strong>
    <% end %>
  </div>
〜省略〜

if current_userとすることで、ログインしていればコメント欄を表示し、ログインしていない状態ではテキストを表示するようにしています。
コントローラーも編集していきます。
【例】

class TweetsController < ApplicationController
〜省略〜
  def show
    @comment = Comment.new
    @comments = @tweet.comments.includes(:user)
  end
〜省略〜
end

comments#createにアクション先を飛ばしたいので、@comment = Comment.newとインスタンス生成をします。
tweetsテーブルとcommentsテーブルはアソシエーションが組まれているので、@tweet.commentsとすることで、@tweetについて投稿された全てのコメントを取得することができます。
ビューではどのユーザーのコメントかを明らかにするために、アソシエーションを使ってユーザーのレコードを取得する処理をします。includesメソッドはN+1問題解消のために使用しています。
次はtweets/show.html.erbにビューを記述していきます。

app/views/tweets/show.html.erb
〜省略〜
<div class="XXXX">
      <h4>コメント一覧</h4>
      <% if @comments %>
        <% @comments.each do |comment| %>
          <p>
            <strong><%= link_to comment.user.nickname, "/users/#{comment.user_id}" %>:</strong>
            <%= comment.text %>
          </p>
        <% end %>
      <% end %>
    </div>
〜省略〜

if @commentsで、もし@commentsが空だった場合でもエラーが起こらないようにしています。
@commentsには複数のコメントが入っているのでeachメソッドを使ってます。
コメントをしたユーザー名をクリックしたらそのユーザーページに遷移するようにlink_toメソッドを使っています。
ユーザーのidはcomment.user_idと記述することで、コメントを投稿したユーザーのidをparamsとして送ります。

2
9
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
2
9