1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

rails_tutorial chapter8 <忘備録>ログインとログアウト

Posted at

ルーティングの一覧

1 2 3 4 5
HTTPリクエスト URL 名前付きルート アクション名 用途
GET /login login_path new 新しいセッションのページ(ログイン)
POST /login login_path create 新しいセッションの作成(ログイン)
DELETE /logout logout_path destroy セッションの削除(ログアウト)

ユーザー登録フォームでform_withヘルパーを使い、ユーザーのインスタンス変数@userを引数にとっていました。
すると、railsが勝手に「フォームのactionは/usersというURLへのPOSTである」と解釈をして、createまで実行してくれました。

<%= form_with(model: @user, local: true) do |f| %>
  .
  .
  .
<% end %>

しかし、ログインの場合はモデルを介していないので、railsに「どこに、何を?」を指定しなければいけません。

セッションの場合はリソースのスコープ(ここではセッション)とそれに対応するURLを具体的に指定する必要があります。

以下の場合、[:session]をlogin_pathに送るということになります。ちなみにscopeの値は任意なので、分かりやすければなんでも構わないです。

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

全体的には以下の通り

app/views/sessions/new.html.erb
<h1>Log in</h1>

<div class="row">
  <div class="col-md-6 col-md-offset-3">
    <%= form_with(url: login_path, scope: :session, local: true) do |f| %>

      <%= f.label :email %>
      <%= f.email_field :email, class: 'form-control' %>

      <%= f.label :password %>
      <%= f.password_field :password, class: 'form-control' %>

      <%= f.submit "Log in", class: "btn btn-primary" %>
    <% end %>

    <p>New user? <%= link_to "Sign up now!", signup_path %></p>
  </div>
</div>

htmlは以下の通り

<form accept-charset="UTF-8" action="/login" method="post">
  <input name="authenticity_token" type="hidden"
         value="NNb6+J/j46LcrgYUC60wQ2titMuJQ5lLqyAbnbAUkdo=" />
  <label for="session_email">Email</label>
  <input class="form-control" id="session_email"
         name="session[email]" type="email" />
  <label for="session_password">Password</label>
  <input id="session_password" name="session[password]"
         type="password" />
  <input class="btn btn-primary" name="commit" type="submit"
       value="Log in" />
</form>

name='◯◯◯'の◯◯◯部分が重要ですね。
f.email_field :email → session[email] となって、
これを取り出すには、params[:session][:email]となります。
(パスワードも同様、params[:session][:password])

log_inメソッド(引数あり)を作成

module SessionsHelper

  # 渡されたユーザーでログインする
  def log_in(user)
    session[:user_id] = user.id
  end
end

ログインを実装

ユーザーが存在しているかまず確認し、存在していれば、それをフォームで送られてきたパスワードをauthenticatedメソッドで実行。うまくいけば、session[:user_id] = user.idが実行。

class SessionsController < ApplicationController

  def new
  end

  def create
    user = User.find_by(email: params[:session][:email].downcase)
    if user && user.authenticate(params[:session][:password])
      log_in user
      redirect_to user
    else
      flash.now[:danger] = 'Invalid email/password combination'
      render 'new'
    end
  end

  def destroy
    logout
  redirect_to root_url
  end
end

ユーザーIDが存在しない状態でfindを使うと例外が発生してしまいます。
findのこの動作は、プロフィールページでは適切でした。
IDが無効の場合は例外を発生してくれなければ困るからです。

しかし、「ユーザーがログインしていない」などの状況が考えられる今回のケースでは、session[:user_id]の値はnilになりえます。この状態を修正するために、createメソッド内でメールアドレスの検索に使ったのと同じfind_byメソッドを使うことにします。ただし今度はemailではなく、idで検索します。

User.find_by(id: session[:user_id])

nilガードをしてもよさそうだが、それよりもfind_byにする方が手っ取り早い。

current_user / logged_in? / log_outを実装

module SessionsHelper

 # 渡されたユーザーでログインする
 def log_in(user)
   session[:user_id] = user.id
 end

 # 現在ログイン中のユーザーを返す(いる場合)
 def current_user
   if session[:user_id]
     @current_user ||= User.find_by(id: session[:user_id])
   end
 end

 # ユーザーがログインしていればtrue、その他ならfalseを返す
 def logged_in?
   !current_user.nil?
 end

 # 現在のユーザーをログアウトする
 def log_out
   session.delete(:user_id)
   @current_user = nil
 end
end

なぜ、@current_user = nil が必要なのか?
session.delete[:user_id]で十分そうだけど。

リンクを作成

<%= link_to "Profile", current_user %>

なお、上のコードは省略形で、このように書くこともできます。

<%= link_to "Profile", user_path(current_user) %>

method: :deleteと指定しているにも関わらず、なぜかroutingエラーが発生。

「getのrouteは存在しません」と。それはそうです。分かってるんです。
色々調べたら、application.jsのujs(控えめなJS)が関係しているようでした。
以下のページを参考にさせてもらいました。ありがとうございました。
https://qiita.com/natu_kumo_/items/8ef3343fda6715ed1d1a
https://qiita.com/yoshinyan/items/194d3c4dcc0f246b1ad1

ログインとログアウト機能の実装が完了しました。
8章では、セッションを利用して、ログインできるようになりました。

以下の3点が最も重要だったかと思います。
1、モデルを介していないため、form-withでurlとscopeを使用すること
2、ログアウトのlink_toではmethod: :deleteでdestroyアクションへ飛ばすこと
3、current_userやlogged_in?をSessionsHelperに作成し、表示を切り替える(記事では割愛しました。)

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?