投稿アプリのコメント機能

コメント機能の実装です
正直ここはすごく苦手です。。。
Xの素晴らしい出来に改めて尊敬です！

### 目的
- アソシエーションを復習
- ルーティングのネストの理解

コメントは、ツイートと別のテーブルで管理しなくてはなりません。
なのでコメントテーブルを作る必要があります
さらに、コメントはどの投稿に対してのコメントなのか、
誰の投稿したコメントなのか明示されている必要があります
そのため、userモデルとtweetモデルの２つにアソシエーションを組む必要があります

### Commentモデル作成
`rails g model comment`

#### マイグレーション編集
db/migrate/20XXXXXXXXXXXX_create_comments.rb
```ruby
lass CreateComments < ActiveRecord::Migration[7.0]
  def change
    create_table :comments do |t|
      t.integer :user_id
      t.integer :tweet_id
      t.text :text
      t.timestamps
    end
  end
end

```
「誰が投稿したコメントなのか」`user_id`
「どのツイートに対してのコメントなのか」`tweet_id`

`rails db:migrate`

tweet 「１対多」　comment
users 「１対多」　comment

### アソシエーション定義
app/models/comment.rb
```ruby
class Comment < ApplicationRecord
  belings_to :tweet
  belongs_to :user
end


```
app/models/tweet.rb

```ruby
lass Tweet < ApplicationRecord
  validates :text, presence: true
  belongs_to :user
  has_many :comments  # commentsテーブルとのアソシエーション
end
```
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  # commentsテーブルとのアソシエーション
end
```

### createアクションのルーティングを設定
#### ルーティングのネスト
ある記述の中に別の記述をして、親子関係を示す方法「入れ子構造」
ルーティングをネストさせる一番の理由は、アソシエーション先のレコードのidをparamsに追加してコントローラーに送るためです

config/routes.rb
```ruby
Rails.application.routes.draw do
  devise_for :users
  root to: 'tweets#index'
  resources :tweets do
    resources :comments, only: :create
  end
  resources :users, only: :show
end
```

`tweet_comments POST   /tweets/:tweet_id/comments(.:format)   comments#create`
`:tweet_id`の箇所へ、コメントと結びつくツイートのidを記述すると、paramsの中に`tweet_id`というキーでパラメーターが追加され、コントローラーで扱うことができます

### commentsコントローラーを作成
`rails g controller comments`
### createアクションをコントローラーに定義
app/controllers/comments_controller.rb
```ruby
class CommentsController < ApplicationController
  def create
    Comment.create(comment_params)
  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メソッドの引数では、ストロングパラメーターを用いて保存できるからむが指定しています

渡されたparamsの中にcommentというハッシュがある二重構造になっているため、requireメソッドの引数に指定して、textを取り出しました。
user_idカラムには、ログインしているユーザーのidとなる`current_user.id`を保存し、
tweer_idカラムは、paramsで渡されるようにするので、`params[:tweet_id]`として保存

app/controllers/comments_controller.rb
```ruby
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
```
redirect_toの後に亜h、ルーティングのURLやPrefixを記述することで、そのアクションを実行できます

tweetsコントローラーのshowアクションを実行するには、ツイートidが必要です
そのため、ストロングパラメーターを用いて上で変数commentに代入します
リダイレクト先の指定には、アソシエーションを利用して、commentと結びつくツイートのidを記述しています

### コメント投稿フォームを投稿詳細に追加
app/views/tweets/show.html.erb
```ruby
<div class="contents row">
  <p><%= @nickname %>さんの投稿一覧</p>
  <% @tweets.each do |tweet| %>
    <%= render partial: "tweets/tweet", locals: { tweet: tweet } %>
  <% end %>
  <div class="container">
    <% if user_signed_in? %>
      <%= form_with(model: [@tweet, @comment], local: true) do |form| %>
        <%= form.text_area :text, placceholder: "コメントする"、rows: "2" %>
        <%= form.submit "SEND" %>
      <% end %>
    <% else %>
      <strong><p>コメントの投稿には新規登録/ログインが必要です</p></strong>
    <% end %>
  </div>
</div>
```
条件分岐により、ログインしていない状態では投稿フォームを出さずにテキストを表示するようにしています。

### コメント表示欄を投稿詳細に追加
ツイートの詳細画面でツイートと結びつくコメントを表示するためには、ビューを呼び出す前のコントローラーが実行されている時点で、まずはコメントのレコードをデータベースから取得する必要があります
app/controllers/tweets_controller.rb
```ruby
~略~
  def show
    @comment = Comment.new
    @comments = @tweet.comments.includes(:user)
  end
~略~
```

`tweets/show.html.erb`でform_withを使用して、`comments#create`を実行するリクエストを飛ばしたいので、`@comment = Comment.new`というインスタンス変数を生成しなければなりません。

tweetsテーブルとcommentsテーブルはアソシエーションが組まれているので、`@tweet.comments`とすることで、`@tweet`へ投稿された全てのコメントを取得
アソシエーションを使ってユーザーのレコードそ取得する処理を繰り返します。
その時に「N＋１問題」が発生してしまうので、`includesメソッド`を使って、N+1問題を解決している点にも注意

app/views/tweets/show.html.erb
```ruby
    <div class="comments">
      <h4>＜コメント一覧＞</h4>
      <% @comments.each do |comment| %>
        <p>
          <strong><%= link_to comment.user.nickname, "/users/#{comment.user_id}" %>：</strong>
          <%= comment.text %>
        </p>
      <% end %>
    </div>
```
`@comments`には複数のコメントのレコードが入っているので、配列の形をとっています。
そのため、ビューに表示させるためには`eachメソッド`を使って、一つ一つのレコードを分解してから表示させます。
コメントしたユーザーの名前をクリックしたら、そのユーザーのマイページへ遷移します。
そのために、名前のところへ`link_toメソッド`を使ってリンクを作りました

ユーザーのidは`comment.user_id`とパスに記述することで、コメントを投稿したユーザーのidをparamsで扱えるようにしてます