ツイートにユーザー情報を追加する
作業内容
- tweetsテーブルにカラムを追加する
- ツイート保存時にユーザー情報を追加する処理を記述する
- tweetsテーブルにカラムを追加
- tweetsテーブルに
user_id
カラムをinteger
型で追加する。
$ rails g migration AddUserIdToTweets user_id:integer
# tweetsテーブルにuser_idカラムをinteger型で追加するマイグレーションファイルの作成
$ rake db:migrate
# マイグレーションファイルの実行
#1. ツイート保存時にユーザー情報を追加する処理をする
● current_user
deviseでログイン機能を実装すると、current_user
というヘルパーメソッドを使用することができる。これは、現在ログイン中のユーザーのレコードを、userクラスのインスタンスとして取得することができるメソッドである。
例を上げるとusersテーブル
でのid
カラムの値が1
のアカウントでログインしている場合、current_user
はUser.find(1)
と同じ意味を持つ。
この時、以下の例のようにcurrent_user.usersテーブルのカラム名
とすることで、ログイン中のユーザーの情報として登録されている各カラムの値を取得することができる。
【例】
# current_userを利用して、ログインしているユーザーのデータを取得する
current_user.name
=> "nakamura"
current_user.email
=> "hoge@sample.com"
-
tweetsテーブルのuser_idカラムに保存すべきなのはcurrent_userのidカラムの値
になる。 - ツイートを保存する際に、
image
,text
というビューから送られてくる情報に加えて、user_id
カラムにログイン中のユーザーのidを保存しなければならない。
class TweetsController < ApplicationController
before_action :move_to_index, except: :index
def index
@tweets = Tweet.page(params[:page]).per(5).order("created_at DESC")
end
def new
end
def create
# tweet_paramsというストロングパラメーターを使用
Tweet.create(image: tweet_params[:image], text: tweet_params[:text], user_id: current_user.id)
private
def tweet_params
params.permit(:image, :text)
end
def move_to_index
redirect_to action: :index unless user_signed_in?
end
end
- createアクションの__user_id: current_user.id__は現在ログイン中のユーザーの
id
を保存するための記述になる。
2. マイページの作成
- マイページのルーティングを記述
- コントローラとアクションを作成
- マイページ用のビューファイルを作成
routes.rbの編集
Rails.application.routes.draw do
devise_for :users
root 'tweets#index"
get 'tweets' => 'tweets#index'
get 'tweets/new' => 'tweets#new'
post 'tweets' => 'tweets#create'
get 'users/:id' => 'users#show'
end
コントローラーとアクションを作成
● whereメソッド
where
メソッドはActiveRecordメソッドのうちの一つであり、モデル.where(条件)
のように引数部分に条件を指定することで、テーブル内の__条件に一致したレコードのインスタンスを配列型で取得__できる。
また、where
メソッドを連続して記述することで複数の条件に一致したレコードも取得できる。
【例】
[1] pry(main)> Tweet.where('id < 3')
=> [#<Tweet id: 1, image: "test1.jpg", text: "ナイス!", created_at: "2019-09-27 00:00:00", updated_at: "2019-09-27 00:00:00", user_id: 1>,#<Tweet id: 2, image: "test2.jpg", text: "Thank you!", created_at: "2014-12-07 00:00:00", updated_at: "2014-12-07 00:00:00", user_id: 2>]
# idが3未満のtweetsテーブルのインスタンスを配列で取得
[2] pry(main)> Tweet.where('id < 3').where(user_id: 1)
=> [#<Tweet id: 1, image: "test1.jpg", text: "いい景色だ。", created_at: "2014-12-06 00:00:00", updated_at: "2014-12-06 00:00:00", user_id: 1>]
# idが3未満かつuser_idが1のtweetsテーブルのインスタンスを配列で取得
users_controllerの作成
$ rails g controller users
usersコントローラーのshowアクションにマイページに表示したい情報を定義
class UsersController < ApplicationController
def show
# 現在ログインしているユーザーのニックネーム
@nickname = current_user.nickname
# 現在ログインしているユーザーが投稿したツイート
@tweets = Tweet.where(user_id: current_user.id).page(params[:page]).per(5).order("created_at DESC")
end
end
マイページ用のビューファイルを作成
#省略
<header class="header">
<div class="header__bar row">
<h1 class="grid-6"><a href="/">Tweet</a></h1>
<% if user_signed_in? %>
<div class="user_nav grid-6">
<span><%= current_user.nickname %>
<ul class="user__info">
<li>
<a href="/users/<%= current_user.id %>">マイページ</a>
<%= link_to "ログアウト", destroy_user_session_path, method: :delete %>
</li>
</ul>
</span>
<a class="post" href="/tweets/new">投稿する</a>
</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>
#省略
次回からはaタグの記述はヘルパーを使うようにする。
アソシエーションの利用
- アソシエーションの定義
- アソシエーションの実装
アソシエーションを定義する
- モデルクラスには__has_many__や__belogns_to__などの定義がされている。
- 所属する側のテーブルに__所属するクラス名_id__というカラムがある。(
user_id
というカラムをtweetsテーブルに追加してあるため)
userの作成したtweetが複数個(has_many :tweets
)
class User < ApplicationRecord
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :validatable
has_many :tweets
end
tweetはいずれかのuserに属する(belongs_to :user
)
class Tweet < ApplicationRecord
belongs_to :user
end
以上でアソシエーションの定義をしたことになる。
アソシエーションを実装
【例】 アソシエーションをしない場合
$ rails c
[1] pry(main)> user = User.find(1)
[2] pry(main)> Tweet.where(user_id: user.id)
=> [#<Tweet id: 1, image: "http://photo1.jpg", text: "ナイス!", created_at: "2014-12-06 09:00:00", updated_at: "2014-12-06 09:00:00", user_id: 1>,
#<Tweet id: 1, image: "http://photo2.jpg", text: "beautiful!", created_at: "2014-12-06 10:00:00", updated_at: "2014-12-06 10:00:00", user_id: 1>]
【例】 アソシエーションを使用
$ rails c
[1] pry(main)> user = User.find(1)
[2] pry(main)> user.tweets
=> [#<Tweet id: 1, image: "http://photo1.jpg", text: "ナイス!", created_at: "2014-12-06 09:00:00", updated_at: "2014-12-06 09:00:00", user_id: 1>,
#<Tweet id: 1, image: "http://photo2.jpg", text: "beautiful!", created_at: "2014-12-06 10:00:00", updated_at: "2014-12-06 10:00:00", user_id: 1>]
アソシエーションを利用した@tweets
を定義する
class UsersController < ApplicationController
def show
@nickname = current_user.nickname
@tweets = current_user.tweets.page(params[:page]).per(5).order("created_at DESC")
end
end
上記のように__current_user.tweets__と続ければ、__現在ログインしているユーザーの投稿したツイート全て__を取得できる。
ツイートからユーザー情報を先読み
● n+1問題
-
n+1問題
とは、データを呼び出す際に大量のSQLが発行されてしまう問題のことである。例にあげると
tweetsのindexアクションで全ツイートを取得する一回に加えて、アソシエーションを利用してツイートの数だけユーザー情報をその度に呼び出してしまう。
よって、その状態だとツイート数+1回SQLが発行されてしまう。この状態のことをn+1
問題と言う。
● includesメソッド
-
n+1問題
は__includes__メソッドを使うことによって解消することができる。指定された関連モデルをまとめて取得することで、SQLの発行回数を減らすことができる。 - 記述の仕方は__inludes(:モデル名)__のように引数で、関連モデルをシンボル型で指定する。
includesメソッドを追記
class TweetsController < ApplicationController
def index
@tweets = Tweet.includes(:user).page(params[:page]).per(5).order("created_at DESC")
end
#以下省略
end
まとめ
久しぶりに上記関係の復習をやってみましたが以前はなんのこっちゃ?みたいな部分があったのですがゆっくり読み返したことによって理解できたのではないかと思っています。
やはりこうしたアウトプットは重要だと痛感しました。
説明不足で端折ってる部分もあるかと思いますが、自分の備忘録としてこの記事を残させていただきます。