こちらも備忘録です!
目的
- アソシエーションの理解
- 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
~略~
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
~略~
has_many :tweets
end
belongs_toメソッド
TweetモデルとUserモデルとの間に「1対1」のつながりがあることを示す
一つの投稿は一人のユーザーが所属する
「Tweet belongs to User」
app/models/user.rb
~略~
belongs_to :user
end
ユーザーに関するshowアクションのルーティングを設定
~略~
resources :users, only: :show
マイページボタンを投稿一覧に追加
app/views/layouts/application.html.erb
# 省略
<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
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
<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
~略~
<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
~略~
<span class="name">
<a href="/users/<%= @tweet.user.id %>">
<span>投稿者</span><%= @tweet.user.nickname %>
</a>
</span>
~略~
ツイートからユーザー情報も先に読み込む
N+1問題
アソシエーションを利用した場合に限り、
データベースへのアクセス回数が多くなってしまう問題
アプリのパフォーマンス低下
includesメソッド
引数に指定された関連モデルを1度のアクセスでまとめて取得できる
モデル名.includes(:紐づくモデル名)
includeメソッドを使用すると全てのレコードを取得するため、allメソッドは省略可能
app/controllers/tweets_controller.rb
~略~
def index
@tweets = Tweet.includes(:user)
end
~略~
投稿画面のビューを変更
ツイートを表示する際にアソシエーションを利用して投稿者のニックネームが表示されるようになったので、nameカラムは不必要
投稿時に「Nickname」の値を入力する必要なし
app/views/tweets/new.html.erb app/views/tweets/edit.html.erb
<%= form.text_field :name, placeholder: "Nickname" %>←削除
投稿時の処理を変更
投稿時に「name」を入力する必要がなくなったので、それに合わせてtweetsコントローラーの処理も変更
nameカラムはもう使用しないので、ツイートの保存時にnameカラムへ情報を保存しないよう変更を行います
app/controllers/tweets_controller.rb
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
<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
class UsersController < ApplicationController
def show
user = User.find(params[:id])
@nickname = user.nickname
@tweets = user.tweets
end
end