チワワかわいいブログ

RUNTEQでrails勉強する日々の記録

2020/12/14 railsチュートリアル8章

セッション

sessionsコントローラ

モデルは使わずに、モデル(データベース)以外のところにデータを保管する。
サーバーにある、保存する場所=sessionsに保管する。
sessionsに保存するデータを利用して、ログインしているかどうかわかる仕組みを作る。
sessionsメソッド -- session[:キー]にバリューを渡すとsessionsに保存できる

form_with②
form_with(url: login_path, scope: :session, local: true)

今回はモデルを使わないから、URL指定する
どこに何を送ればいいのか
URL login_pathにscope: :sessionを送る(なんでもいい) scope: に指定したparamsのハッシュに入る→params[:session]になる。
前回使ったこれは、

form_with(model: @user, local: true)

modelに指定している@userの中身をみる
@user→Userモデルのオブジェクト
@user→newメソッドで作ってる
なので、Userモデル(/users)に、作成する(POST)リクエストを送ればいいと判断してくれる
今回はモデルがないので、送り先、送るものを指定する必要がある

ユーザーを特定する
 user = User.find_by(email: params[:session][:email])
    if user && user.authenticate(params[:session][:password])

もし、find_byでemailが合致するデータがなかった場合、user = nil となる。
この時user.authenticateの部分はuser=nilに対してautenticateメソッドをよぶことになるのでエラーになる。
なので、user &&の部分でまずnilでないことを確認する。

エラーメッセージ

モデルはないので、errorメソッドは使えないのでflashを使う。

flash[:danger] = 'Invalid email/password combination' 

flashは次のリクエストが来るまで表示される。
今回はredirect_toではなくrenderを使っているが、renderはリクエストが発生せず直接テンプレートを描画しており、リクエストは発生しないので、このまままでは2回遷移しないとflashが消えない。
ので、flash.nowに直す。flash.nowは次のリクエストがきたら消えてください。

ログイン

session[:user_id]
session[:user_id] = user.id

として、セッションにidを渡してあげると、ブラウザの中と、サーバーの中にデータを保存する。
その2つの情報が一致しているかを確認することで、ログインしているかどうかと判断ができる

ログインしている時にブラウザの表示をかえる
def current_user
  if session[:user_id]
    User.find_by(id: session[:user_id])
  end
end

find_byはデータベースに問い合わせが入る。
if session[:user_id]でデータベースに問い合わせを入れる前にsessionにデータがあるか確認をすることで問い合わせを減らす=動作が重くなるのを避ける

メモ化
@current_user ||= User.find_by(id: session[:user_id])

さらに軽くしたい
ローカル変数だと、current_userが呼び出せれる度にデータベースに問い合わせが行くことになる
インスタンス変数を使うことで、ゲットリクエストがどこかに飛んだらそのリクエストが続く間は生きてる
一回のリクエストの中で何回もcurrent_userメソッドが呼ばれても、そのリクエストの間インスタンス変数は生きているから問い合わせが何回も出ることを防げる
わかりやすいコードで書く

if @current_user.nil?
  User.find_by(id: session[:user_id])
  return @current_user
else
  return @current_user
end
a = a || b # aがfalseのときbが実行される、aがtrueのときbは実行されない
a = a && b # aがtrueのときbが実行される、aがfalseのときbは実行されない
a ||= b     => a = a ||(or) b

rails consoleで何も代入しない状態で呼び出すと
@current_user => nil
current_user => エラー

<%= link_to "Profile", current_user %>

user_path(current_user.id)の略

<%= link_to "Log out", logout_path, method: :delete %>

link_toメソッドで送られるリクエストは基本的にGET
logoutはDELETEである必要があるため(destroyアクションに繋ぐため)オプションでリクエストをする

logged_in?メソッド
  def logged_in?
    !current_user.nil?
  end

current_userがnil?ではない(!)