ログインとは、サイトを操作しているユーザーが誰であるかが判別できる状態のことをさします。ログインしていることにより、操作しているユーザーに合わせて、同じURLでもユーザーごとに表示を変えることなどができます。
例えば、Progateのマイページでもユーザーによって表示される内容(レベルなど)が変わっていますね。
ログインページを作成しよう
まずはログインページを作るためにルーティング、アクション、ビューを追加しましょう。 今回は「localhost:3000/login」でlogin_formアクションを呼び出せるようにします。
パスワード用のフォームを追加する
login_form.html.erbにパスワード用のフォームを追加しましょう。inputタグのtype属性をpasswordとすると右の図のように、入力したパスワードが伏字となるパスワード用のフォームになります。
パスワード用のフォームを追加する
login_form.html.erbにパスワード用のフォームを追加しましょう。inputタグのtype属性をpasswordとすると右の図のように、入力したパスワードが伏字となるパスワード用のフォームになります。
routes.rb
get “login” => “users#login_form”
users_controller.rb
def login_form
end
login_form.html.erb
application.html.erb
<%= link_to("ログイン", "/login") %>
パスワードカラムの追加
rails g migration
まずは左の図のように「rails g migration」を用いて、add_password_to_usersというファイル名のマイグレーションファイルを作成しましょう。
マイグレーションファイルの中身は右の図のようにし、「rails db:migrate」を実行しましょう。
rails g migration add_password_to_users
rails db:migrate
バリデーションをかけよう
ユーザーの情報には必ずpasswordの値が存在してほしいので、passwordカラムにもバリデーションをかけましょう。
「存在するかどうか」のバリデーションは「presence」を使えばできましたね。
add_column :users, password, :string
■ ターミナルで以下のコマンドを実行してください
rails g migration add_password_to_users
rails db:migrate(マイグレーションファイル編集後)
rails console(rails db:migrate実行後)
■ コンソールで以下のコードを実行してください
user = User.find_by(id: 1)
user.password = “好きなパスワード”
user.save
add_password_to_users.rb
class AddPasswordToUsers < ActiveRecord::Migration[5.0]
def change
add_column :users, :password, :string
end
end
user.rb
class User < ApplicationRecord
validates :name, {presence: true}
validates :email, {presence: true, uniqueness: true}
# passwordカラムにバリデーションを設定してください
validates :password, {presence: true}
end
フォームの値を送信できるようにしよう
ルーティングとアクションの追加
post
フォームから送信された値を受け取るために、ルーティングとアクションを追加しましょう。フォームの値を送信するため、ルーティングはpostとすることに注意しましょう。
フォームの送信先を指定しよう
form_tagメソッドを用いて、login_form.html.erbにフォームの送信先を指定しましょう。また、フォームに入力された値がRails側に送信されるように、inputタグにname属性を追加しましょう。
loginアクションのルーティング
ルーティング
「/login」で2つのルーティングが被っているように見えますが、「get」と「post」では異なるルーティングとして扱われるので問題ありません。(link_toメソッドではデフォルトでgetのルーティングを探し、form_tagメソッドがデフォルトでpostのルーティングを探します。)
routes.rb
# ルーティングを追加してください
post "login" => “users#login”
users_controller.rb
# loginアクションを追加してください
def login
end
login_form.html.erb
<%= form_tag("/login") do %>
メールアドレス
パスワード
<% end %>
5. ユーザーを特定しよう
ログインするユーザーを特定する
フォームに入力されたメールアドレスとパスワードはparams[:email]とparams[:password]で受け取れます。usersテーブルから入力された値に
一致するユーザーを取得し、変数@userに代入しましょう。
また、図のようにfind_byメソッドは引数をコンマ( , )で区切ることで複数の条件からデータベースを検索することができます。
def login
# 入力内容と一致するユーザーを取得し、変数@userに代入してください
@user = User.find_by(email: params[:email], password: params[:password])
# @userが存在するかどうかを判定するif文を作成してください
if @user
flash[:notice] = “ログインしました”
redirect_to(“/posts/index”)
else
render(“users/login_form”)
end
end
ユーザーが存在しない場合の処理
エラーメッセージの表示
ログインフォームに入力されたメールアドレスとパスワードに一致するユーザーが存在しない場合は、エラーメッセージを表示しましょう。今回のエラーメッセージはバリデーションのエラーメッセージとは異なり、「find_byメソッドで検索したが存在しなかった」という結果を伝えるためのものなので、図のように自作する必要があります。
初期値の設定
変数@emailと@passwordを定義し、それぞれにparams[:email]とparams[:password]の値を代入し、フォームに入力した値が初期値となるようにしましょう。また、変数に代入した初期値を表示するためにフォームにvalue属性を追加しましょう。
<% if @error_message %>
<% end %>
<%= form_tag("/login") do %>
メールアドレス
<% end %>
ログイン処理を書いていこう
変数session(1)
session
ページを移動してもユーザー情報を保持し続けるために、sessionという特殊な変数を用います。sessionに代入された値は、ブラウザ(InternetExplorer, GoogleChrome等)に保存されます。sessionに値を代入すると、ブラウザはそれ以降のアクセスでsessionの値をRailsに送信します。
変数session(2)
session
具体的にsessionに値を代入するときには、左の図のようにuser_idをキーとし、値を代入します。右の図のように、@userが存在する場合に変数sessionに@user.idを代入することで、特定したログインユーザーの情報が保持され続けます。
users_controller.rb
if @user
# 変数sessionに、ログインに成功したユーザーのidを代入してください
session[:user_id] = @user.id
flash[:notice] = “ログインしました”
redirect_to(“/posts/index”)
else
application.html.erb
<% if session[:user_id] %>
<%= session[:user_id] %>
ログアウト機能を作ろう
今度は逆に、ログアウト機能をつくってみよう。
ログインの際は変数 session にログインするユーザーの id を代入したことで、「ログイン中」という状態が保持できていたな。
ログアウトはその逆で、変数 session の
user_id の値を削除してしまうのじゃ。
session[:user_id]の値を空にしよう
sessionログアウトnil
ログアウトする、つまり「ログイン状態でなくする」にはsession[:user_id]の値を空にします。図のようにsession[:user_id]にnilを代入することで、session[:user_id]の値を空にすることができます。
getとpostgetpostsession
今回はデータベースを変更するわけでも、 form_tag メソッドでもないのにルーティングを post にするのですか?
データベースを変更するときに加え、 session の値を変更する場合でも post を用いることになっておるのじゃ。
ヘッダーのメニューの表示内容を切り替えよう
現在ログイン状態でもヘッダーにログインページへのリンクが表示されますが、ログイン状態ではこのリンクは不要ですね。ログイン前後でsession[:user_id]の値が異なることを利用して、このようなリンクを表示するかどうかを切り替えるようにしましょう。
# ログアウト用のルーティングを追加してください
post “logout” => “users#logout”
# アクションを追加してください
def logout
session[:user_id] = nil
flash[:notice] = “ログアウトしました”
redirect_to(“/login”)
end
<%= link_to("ログアウト", "/logout", {method: :post}) %>
ユーザー登録時にパスワードを保存する
左の図のように、ユーザー登録フォームにパスワード用のフォームを追加しましょう。また、ユーザー登録時にpasswordカラムの値が設定されるようにしましょう。そのために、createアクションのnewメソッドの引数としてpasswordを追加するようにします。
アクション側で共通の変数を定義しよう
ヘッダーで current_user という変数を定義したが、変数の定義はアクション側でするべきじゃな。
そのための方法を見ていくが、まずは application.html.erb の仕組みについて復習しよう。
application.html.erbの仕組み
yieldapplication.html.erb
各アクションに対応したビューファイルは、application.html.erbの<%= yield %>部分に代入され表示されています。これにより、application.html.erbは全アクションから呼び出されます。
before_action
before_action
各コントローラの全アクションで共通する処理がある場合には、before_actionを使うと便利です。before_actionを用いることで、アクションが呼び出される際に必ずbefore_actionの処理が実行されます。
これにより、全アクションで共通する処理を1箇所にまとめることができます。
applicationコントローラ
applicationコントローラapplication_controller.rb
全てのコントローラで共通する処理はapplicationコントローラにまとめることができます。図のようにログイン中のユーザーを取得するset_current_userメソッドを定義し、before_actionに指定しましょう。これで、全コントローラの全アクションで@current_userを定義することができます。
application_controller.rb
class ApplicationController < ActionController::Base
# before_actionにset_current_userメソッドを指定してください
before_action :set_current_user
# set_current_userメソッドを定義してください
def set_current_user
@current_user = User.find_by(id: session[:user_id])
end
end
application.html.erb