##はじめに
railsチュートリアルで理解しにくいところや、詰まったところを書いていく記事になります。
なので、手順を示す記事とはなっていません。
##セッション
HTTPはステートレス (Stateless) なプロトコルであり、ブラウザのあるページから別のページに移動したときに、ユーザーのIDを保持しておく手段がHTTPプロトコル内にはまったくない。
よって、ユーザーログインの必要なwebアプリケーションではセッション (Session)
と呼ばれる半永続的な接続をコンピュータ間 (ユーザーのパソコンのWebブラウザとRailsサーバーなど) に別途設定する必要がある。
###セッションコントローラ
まずはログインのフォームをnewアクションで処理するような、コントローラを作成する。
$ rails generate controller Sessions new
create
や destroy
といったアクションも扱うが、ビューファイルが必要ないため、ここでは newアクション
だけを一緒に生成しておく。
routes.rbでは、Usersリソースのように resourcesメソッド
を使ってRESTfulなルーティングを自動的にフルセットで利用することはないので、**「名前付きルーティング」**だけを使う。
Rails.application.routes.draw do
root 'static_pages#home'
get '/help', to: 'static_pages#help'
get '/about', to: 'static_pages#about'
get '/contact', to: 'static_pages#contact'
get '/signup', to: 'users#new'
#追加
get '/login', to: 'sessions#new'
post '/login', to: 'sessions#create'
delete '/logout', to: 'sessions#destroy'
resources :users
end
このように名前付きルーティングが増えてきた場合は、以下のコマンドを打つことで現状のルーティングを確認することができる。
$ rails routes
###ログインフォーム
コントローラとルーティングを定義した次はビューファイルを整えていく。
ログインフォームでは、会員登録をまだしていない人のために、登録のリンクも作っておく。
<% provide(:title, "Log in") %>
<h1>Log in</h1>
<div class="row">
<div class="col-md-6 col-md-offset-3">
<%= form_for(:session, url: login_path) 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>
このソースではform_forタグを使用しているが、form_tagも同じく使用可能
セッションフォームとユーザー登録フォームの違いとして、セッションにはSessionモデルというものがなく、そのため@userのようなインスタンス変数に相当するものもない。
したがって、新しいセッションフォームを作成するときには、form_forヘルパーに追加の情報を独自に渡す必要がある。↓
<%= form_for(:session, url: login_path) do |f| %>
以下に、 HTMLフォームを示しておく。
<form accept-charset="UTF-8" action="/login" method="post">
<input name="utf8" type="hidden" value="✓" />
<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="text" />
<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>
コントローラで、それぞれのアクションを定義する。
class SessionsController < ApplicationController
def new
end
def create
render 'new'
end
def destroy
end
end
また、createアクション
でユーザーを検証する処理を書く。
def create
user = User.find_by(email: params[:session][:email].downcase)
if user && user.authenticate(params[:session][:password])
# ユーザーログイン後にユーザー情報のページにリダイレクトする
else
# エラーメッセージを作成する
render 'new'
end
end
セッションでは、ユーザー登録のように Active Recordモデル
を使っていないため、自分でエラーメッセージを作る必要がある。
def create
user = User.find_by(email: params[:session][:email].downcase)
if user && user.authenticate(params[:session][:password])
# ユーザーログイン後にユーザー情報のページにリダイレクトする
else
#追加
flash.now[:danger] = 'Invalid email/password combination'
render 'new'
end
end
↑では、flash[:danger]としてしまうと、一度表示されたメッセージが消えなくなってしまうため
flash.now[:danger]
を使い、一度だけ表示されるようにする。
##ログイン
まず、SessionsHelper
をコントローラに読みこますことで、セッションに必要なメソッドを使えるようにしておく。
class ApplicationController < ActionController::Base
protect_from_forgery with: :exception
include SessionsHelper
end
###log_inメソッド
railsで定義済みのsessionメソッドを使って、Sessionsヘルパーにlog_inメソッドを定義する。
module SessionsHelper
# 渡されたユーザーでログインする
def log_in(user)
session[:user_id] = user.id
end
end
これによりsessionコントローラの createアクション
を整えることができる。
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
redirect_to user
はrailsでは自動的に以下のルーティングに変換される。
user_url(user)
###現在のユーザー
セッションIDに対応するユーザーネームを取り出す、 current_userメソッド
を定義する。
if @current_user.nil?
@current_user = User.find_by(id: session[:user_id])
else
@current_user
end
↑のようにすれば、current_userを定義することができるが、もっとコンパクトに定義することができる。
「||=」を使うことで以下のようになる。
@current_user ||= User.find_by(id: session[:user_id])
「||=」は左辺がnilあれば、右辺を代入するという使い方ができる。
よって、current_userメソッド
は以下のように定義することができる。
# 現在ログイン中のユーザーを返す (いる場合)
def current_user
if session[:user_id]
@current_user ||= User.find_by(id: session[:user_id])
end
end
###レイアウトリンクの変更
ユーザーがログインしているかしていないかで、レイアウトを変更する必要がある。
下記のように logged_in?
のメソッドを使えば、レイアウトを変更する必要がある。
<% if logged_in? %>
# ログインユーザー用のリンク
<% else %>
# ログインしていないユーザー用のリンク
<% end %>
よって、logged_in?
を定義する。
current_userがnilでなければログインしている、という風に解釈できるので↓のように定義できる。
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
end
##ユーザー登録時にログイン状態
登録が終われば自動的にログインがすんでいる状態になるようにusersコントローラのcreateアクションを追加修正しておく。
def create
@user = User.new(user_params)
if @user.save
#追加
log_in @user
flash[:success] = "Welcome to the Sample App!"
redirect_to @user
else
render 'new'
end
end
##ログアウト
session.delete(:user_id)
↑で、現在のユーザーをnilにすることができるので、logoutメソッド
を以下のように定義する。
module SessionsHelper
# 現在のユーザーをログアウトする
def log_out
session.delete(:user_id)
#セキュリティ上、念の為次の一行も追加する
@current_user = nil
end
end
次に、Sessionsコントローラの destroyアクション
を定義する。
class SessionsController < ApplicationController
.
.
.
def destroy
log_out
redirect_to root_url
end
end
##終わりに
第八章では、Sessionコントローラでログイン・ログアウトを実装した。
webアプリケーションには必要な機能のためしっかり理解しておこうと思う。
次↓
https://qiita.com/jonnyjonnyj1397/items/9cb25f506404c7ae5b9e