投稿アプリマイページ機能の実装

こちらも備忘録です！

### 目的
- アソシエーションの理解
- N＋1問題の対策を理解

### ツイートにユーザー情報を追加
「誰が投稿したのか」わかるようになり
- 特定のユーザーがつぶやいたツイート一覧を取得できる
- ツイートの削除を行う際につぶやいた本人しか削除できないように設定できる
機能が実現可能になります

#### tweetsテーブルにカラムを追加
tweetsテーブルに新しく`user_id`というカラムを作成する
そこにツイートを投稿したユーザーのidを保存します
#### tweetsテーブルにuser_idカラムをinteger型で追加
ターミナル

`rails g migration AddUserIdToTweets user_id:integer`
`rails db:migrate`

### ツイート保存時にユーザー情報も追加
user_idカラムに、ツイートを投稿した(現在ログイン中の)ユーザーの
idを保存する処理を記述
ここに保存すべき値は、currenr_userのid
#### current_userメソッド
現在ログインしているユーザーの情報を取得できる
#### mergeメソッド
ハッシュを結合させる時に使用するRubyのメソッド

app/controllers/tweets_controller.rb
```ruby
~略~
private
def tweet_params
    params.require(:tweet).permit(:name, :image, :text).merge(user_id: current_user.id)
end
~略~
```
### モデル同士を関連付け
#### アソシエーション
モデルを利用したテーブル同士の関連付けのこと
誰がどの投稿にツイートしたかわかるようになる

#### has_manyメソッド
userと他のモデルとの間に「1対多」のつながりがあることを示すこと。
一人のユーザーは複数の投稿を所有する
「User has many Tweets」
app/models/user.rb
```ruby
~略~
    has_many :tweets
end
```
#### belongs_toメソッド
TweetモデルとUserモデルとの間に「１対１」のつながりがあることを示す
一つの投稿は一人のユーザーが所属する
「Tweet belongs to User」
app/models/user.rb
```ruby
~略~
    belongs_to :user
end
```
### ユーザーに関するshowアクションのルーティングを設定

```ruby
~略~
    resources :users, only: :show
```
### マイページボタンを投稿一覧に追加
app/views/layouts/application.html.erb
```ruby
  # 省略
  <header class="header">
    <div class="header__bar row">
      <h1 class="grid-6"><a href="/">PicTweet</a></h1>
      <% if user_signed_in? %>
        <div class="user_nav grid-6">
          <span><%= current_user.nickname %>
            <ul class="user__info">
              <li>
                <%= link_to "マイページ", "/users/#{current_user.id}" %>
                <%= link_to "ログアウト", destroy_user_session_path, data: { turbo_method: :delete } %>
              </li>
            </ul>
          </span>
          <%= link_to "投稿する", new_tweet_path, class: "post" %>
        </div>
      <% else %>
        <div class="grid-6">
          <%= link_to "ログイン", new_user_session_path, class: "post" %>
          <%= link_to "新規登録", new_user_registration_path, class: "post" %>
        </div>
      <% end %>
    </div>
  </header>
  # 省略
```
### ユーザーに関するshowアクションをコントローラーに定義
`rails g controller users`
app/controllers/users_controller.rb
```ruby
class UsersController < ApplicationController
  def show
    @nickname = current_user.nickname
    @tweets = current_user.tweets
  end
end
```

### マイページのビュー作成
`app/views/users`に`show.html.erb`作成
app/views/users/show.html.erb
```ruby
<div class="contents row">
  <p><%= @nickname %>さんの投稿一覧</p>
  <% @tweets.each do |tweet| %>
    <div class="content_post" style="background-image: url(<%= tweet.image %>);">
      <p><%= tweet.text %></p>
      <span class="name"><%= tweet.name %></span>
    </div>
  <% end %>
</div>
```
### 投稿者となるユーザー名の表示
投稿者名を表示するようにビューを変更
現在、user_idがNULLになっていることによるエラー文発生
undefined method 'nickname' for nil:NilClass
→nicknameが参照できていない
ツイートを投稿した際、user_idの一緒に保存するようにしましたが、Sequel Proで確認すればわかる通り、
それ以前に投稿したツイートはuser_idが`NULL`のままになっている
このとき`tweet.user`は`NULL`となるため、`tweet.user.nickname`と書くと、からのクラスに対してnicknameメソッドが実行されてしまい「そんなメソッドは存在しない」というNoMethodErrorが発生
tweetテーブルのuser_idカラムに値を入れます。

投稿者名のところへ、マイページに飛ぶリンクを設置
app/views/tweets/index.html.erb
```ruby
~略~
<p><%= tweet.text %></p>
      <span class="name">
        <a href="/users/<%= tweet.user.id %>">
          <span>投稿者</span><%= tweet.user.nickname %>
        </a>
        <%= tweet.name %>
      </span>
    </div>
  <% end %>
</div>
```
これで、投稿者のリンクをクリックすると、その投稿者(ログインしてkるユーザー)が
投稿したツイートの一覧が表示されるページに、アクセスできるようになる
app/views/tweets/show.html.erb
```ruby
~略~
    <span class="name">
      <a href="/users/<%= @tweet.user.id %>">
        <span>投稿者</span><%= @tweet.user.nickname %>
      </a>
    </span>
~略~
```
### ツイートからユーザー情報も先に読み込む
#### N+1問題
アソシエーションを利用した場合に限り、
データベースへのアクセス回数が多くなってしまう問題
アプリのパフォーマンス低下
#### includesメソッド
引数に指定された関連モデルを１度のアクセスでまとめて取得できる
`モデル名.includes(:紐づくモデル名)`
includeメソッドを使用すると全てのレコードを取得するため、allメソッドは省略可能
app/controllers/tweets_controller.rb
```ruby
~略~
def index
    @tweets = Tweet.includes(:user)
    end
~略~
```
### 投稿画面のビューを変更
ツイートを表示する際にアソシエーションを利用して投稿者のニックネームが表示されるようになったので、nameカラムは不必要
投稿時に「Nickname」の値を入力する必要なし
app/views/tweets/new.html.erb   app/views/tweets/edit.html.erb
```ruby
<%= form.text_field :name, placeholder: "Nickname" %>←削除
```
### 投稿時の処理を変更
投稿時に「name」を入力する必要がなくなったので、それに合わせてtweetsコントローラーの処理も変更
nameカラムはもう使用しないので、ツイートの保存時にnameカラムへ情報を保存しないよう変更を行います
app/controllers/tweets_controller.rb
```ruby
private
  def tweet_params
    params.require(:tweet).permit(:image, :text).merge(user_id: current_user.id)
  end
```
### tweetテーブルから不要なカラム削除
`rails g migration Removeカラム名From削除もとテーブル名　削除するカラム名:型`

`rails g migration RemoveNameFromTweets name:string`
`rails db:migrate`

app/views/users/show.html.erb
```ruby
<div class="contents row">
  <p><%= @nickname %>さんの投稿一覧</p>
  <% @tweets.each do |tweet| %>
    <div class="content_post" style="background-image: url(<%= tweet.image %>);">
      <p><%= tweet.text %></p>
      <span class="name"><%= tweet.name %></span>  # この行を削除
    </div>
  <% end %>
</div>
```

### 投稿者のマイページにアクセスできるようにする
app/controllers/users_controller.rb
```ruby
class UsersController < ApplicationController
  def show
    user = User.find(params[:id])
    @nickname = user.nickname
    @tweets = user.tweets
  end
end
```