#Rails ログイン機能実装手順 (自分用)
・sorceryをGemfileに追加する
gem ‘sorcery’
・次にbundle install
$ bundle install
・sorceryに追加されたジェネレータを実行して、アプリの構築をする
$ rails g sorcery:install
・マイグレーションファイルとuserモデルが作成される
$ rails db:migrate
・で導入完了
#sorcery:installで生成したUserモデルにバリデーションを追加
・Model
以下のようにバリテーションを追加
class User < ActiveRecord::Base
authenticates_with_sorcery!
validates :password, length: { minimum: 3 }, if: -> { new_record? || changes[:crypted_password] }
validates :password, confirmation: true, if: -> { new_record? || changes[:crypted_password] }
validates :password_confirmation, presence: true, if: -> { new_record? || changes[:crypted_password] }
validates :email, uniqueness: true, presence: true
validates :first_name, presence: true, length: { maximum: 255 }
validates :last_name, presence: true, length: { maximum: 255 }
end
#sorcery:installで生成したマイグレーションファイルに必要なカラムを追加する
・ここではlast_nameとfirst_nammeを追加
class SorceryCore < ActiveRecord::Migration[5.2]
def change
create_table :users do |t|
t.string :email, null: false
t.string :crypted_password
t.string :salt
t.string :last_name, null: false #null: falseは空欄入力では名前とは認めないという意味
t.string :first_name, null: false
t.timestamps null: false
end
add_index :users, :email, unique: true
end
end
・マイグレートする
$ rails db:migrate
#ユーザーの新規登録画面を作成(scaffoldを使ってuserコントローラーを作成。)
$ rails g scaffold_controller user email:string crypted_password:string salt:string first_name:string last_name:string
・usersファイルにある、crypted_password、saltの部分はビューに表示しないので削除してpasswordとpassword_confirmationを代わりに入れる。(GitHubのREADMEにはこの手順で書いてあるが、最初から「rails g scaffold_controller user email:string password:string password_confirmation:string first_name:string last_name:string」で実行してもいいと思う。)
・Controller
class UsersController < ApplicationController
def new
@user = User.new #(新規登録で作ったデータを@userに代入する)
end
def create
@user = User.new(user_params)#(postメソッドでUser.newからのデータが送られてきている)新規登録で作ったデータに(user_params)という引数をつけて@userに代入する
if @user.save#訳:@userが保存できたら
redirect_to login_path #(ログイン出来たら「user_sessionsのnew」に飛ぶ(redirect_to)ようになる。*「user_sessionsについては後に説明)
else#訳:@userが保存できなかったら
render :new #(ログイン出来なければ、usersのnewに戻る(render))
end
end
private
def user_params
params.require(:user).permit(:email, :password, :password_confirmation, :first_name, :last_name, :salt)
#Task.newで作られたparams[:user]からemail、password、password_confirmation、first_name,last_nameだけを受け取るようにする。
end
end
・app/views/users/new.html.erbの編集
<div class="container">
<div class="row">
<div class="col-md-10 offset-md-1 col-lg-8 offset-lg-2">
<h1>ユーザー登録</h1>
<%= form_with model: @user, local: true do |f| %>
<div class="form-group">
<%= f.label :last_name %>
<%= f.text_field :last_name, class: 'form-control' %>
</div>
<div class="form-group">
<%= f.label :first_name %>
<%= f.text_field :first_name, class: 'form-control' %>
</div>
<div class="form-group">
<%= f.label :email %>
<%= f.email_field :email, class: 'form-control' %>
</div>
<div class="form-group">
<%= f.label :password %>
<%= f.password_field :password, class: 'form-control' %>
</div>
<div class="form-group">
<%= f.label :password_confirmation %>
<%= f.password_field :password_confirmation, class: 'form-control' %>
</div>
<%= f.submit '登録', class: 'btn btn-primary' %>
<% end %>
<div class='text-center'>
<%= link_to 'ログインページへ', login_path %>
</div>
</div>
</div>
</div>
・新規登録の画面では、登録するデータをデータベースに保存する必要があるので「form_with model」を使用している。
・ルーティングを定義する
get 'login', to: 'user_sessions#new' #login_pathが使えるようになり、login_pathが使われるとログイン画面('user_sessions#new')に移動する
post 'login', to: 'user_sessions#create' #login_path('user_sessions#new')からのデータを'user_sessions#create'へ受け取って、データベースに保存
delete 'logout', to: 'user_sessions#destroy' #logout_pathが使えるようになる。削除処理を行う。
resources :users, only: %i[new create]#usersではnewとcreateしか使わないのでonlyで指定している
#ログイン画面を作成
% rails g controller UserSessions new create destroy
・ログインに必要なuser_sessions_controllerを作成
・コントローラの親にあたる、application_controllerに共通のメソッドを追加する
class ApplicationController < ActionController::Base
before_action :require_login
private
def not_authenticated
redirect_to login_path, danger: "ログインしてください"
end
end
・before_actionはコントローラー内の全てのアクションが実行される前に、その後に続くメソッド(ここではrequire_login)が実行される。
・require_loginメソッドはloginしているかどうかを検証するメソッド。loginできれば、次に進もうとしていたページにリダイレクトできる。loginできなければ「not_authenticated」メソッドが実行される。
・not_authenticatedメソッドは、require_loginでlogin出来なかった場合に実行されるメソッド。ここでは「redirect_to login_path」となっているのでログイン画面('user_sessions#new')にリダイレクトされる。
・user_sessions_controllerの編集
class UserSessionsController < ApplicationController
skip_before_action :require_login, only: %i[create new] #下記で説明
def new; end
def create
@user = login(params[:email], params[:password]) #emailとpasswordが一致していれば@userにデータが代入される(loginメソッドで検証を行なっている)
if @user #訳:loginメソッドで検証が一致して、@userにデータだ代入されたら
redirect_back_or_to root_path #ログインしたらroot_path(root toなどで指定したページ)にリダイレクトする。
else
render :new #ログイン出来なかったら、ログインページ('user_sessions#new')にリダイレクトされる
end
end
end
#####skip_before_action :require_login, only: %i[create new]の説明
・skip_before_actionでbefore_actionをスキップするという意味になる。つまりここでは、[create new]はログインする際に訪れないといけないページなので、ここで「before_action :require_login」が実行されるとそもそもログイン出来なくなる。なので、「only: %i[create new]」でこのページはログインしているかの検証はしないということになる。
・ログインフォーム作成
<div class="container">
<div class="row">
<div class=" col-md-10 offset-md-1 col-lg-8 offset-lg-2">
<h1>ログイン</h1>
<%= form_with url: login_path, local: true do |f| %>
<div class="form-group">
<%= f.label :email %>
<%= f.text_field :email, class: 'form-control' %>
</div>
<div class="form-group">
<%= f.label :password %>
<%= f.password_field :password, class: 'form-control' %>
</div>
<div class="actions">
<%= f.submit 'ログイン', class: 'btn btn-primary' %>
</div>
<% end %>
<div class='text-center'>
<%= link_to '登録ページへ', new_user_path %>
<a href="#">パスワードをお忘れの方はこちら</a>
</div>
</div>
</div>
</div>
・ログインページでは、新規登録と違いデータベースに保存する必要がなく、パスワードとメールアドレスが一致しているかのデータ検証が目的なので「form_with url」を使用している。
#ログインしている時としていない時のトップページを分ける(logged_in?メソッド)
・logged_in?メソッドはsorceryで使えるようになるメソッドで、ログインしているかどうかを判断するメソッド。
<body>
<% if logged_in? %>
<%= render 'shared/header' %>#ログインしている時のrender先
<% else %>
<%= render 'shared/before_login_header' %>#ログインしていない時のrender先
<% end %>
<%= yield %>
<%= render 'shared/footer' %>
</body>
#ログイン機能をヘッダーに追加する
※Bootstrapで全体的なレイアウトやヘッダーのプルダウンが機能している
<header>
<nav class="navbar navbar-expand-lg navigation navbar-light bg-light">
<button class='navbar-toggler' data-toggle='collapse' data-target='#navbarSupportedContent' aria-controls='navbarSupportedContent' aria-expanded='false' aria-label='Toggle navigation'>
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarSupportedContent">
<ul class="navbar-nav ml-auto main-nav align-items-center">
<li class="nav-item">
<%= link_to 'ログイン', 'login_path', class: 'nav-link' %> #リンク先をlogin_pathまたは、’login’にする
</li>
</ul>
</div>
</nav>
</header>
・リンク先を「login_path」にすることで、コントローラーで設定したように、ログイン画面に飛ぶことができる。
#ログアウト機能をヘッダーに追加する
※Bootstrapで全体的なレイアウトやヘッダーのプルダウンが機能している
def destroy
logout
redirect_to root_path #リダイレクト先をログイン画面に指定する
end
<%= link_to 'ログアウト', logout_path, class: 'dropdown-item', method: :delete %>
・リンク先をログアウトに指定し、deleteメソッドを追加