Ruby on Rails5 VIII

ログインとは、サイトを操作しているユーザーが誰であるかが判別できる状態のことをさします。ログインしていることにより、操作しているユーザーに合わせて、同じ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 %>

<%= @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] %>

  • 現在ログインしているユーザーの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


      <% if @current_user %>


    • <%= link_to(@current_user.name, "/users/#{@current_user.id}") %>
    • ログインしていない場合のアクセス制限
      アクセス制限
      ログインしていない場合でも、左図のようにURLを直接入力するとアクセスできてしまいます。右図のような処理を作成し、@current_userがいない場合にはログインページにリダイレクトするようにしましょう。しかし、この処理は他のアクションや他のコントローラでも使いたいので、applicationコントローラとbefore_actionを用いて処理を共通化させましょう。

      authenticate_user
      メソッドの作成
      左図のようにapplicationコントローラにauthenticate_userというメソッドを作成し、アクセス制限の処理を共通化します。authenticate_userは「ユーザーを認証する」という意味です。また、今回は全アクションに適用したい訳ではないので、各コントローラの各アクションでbefore_actionを適用する方法を見ていきましょう。

      before_actionを特定のアクションのみで実行する
      before_action継承only
      図のように、onlyを用いて各コントローラでbefore_actionを使うことで、指定したアクションでのみそのメソッドを実行することができます。各コントローラは、applicationコントローラを継承しているので、継承元のメソッドを使うことができます。

      @current_user
      @current_userがauthenticate_userメソッドの中でも使用されていることに注目しましょう。@変数で定義した変数は同じクラスの異なるメソッド間で共通して使用することが可能です。

      application_controller.rb
      # authenticate_userメソッドを定義してください
      def authenticate_user
      if @current_user == nil
      flash[:notice] = “ログインが必要です”
      redirect_to(“/login”)
      end
      end

      users_controller.rb
      # before_actionにauthenticate_userメソッドを指定してください
      before_action :authenticate_user, {only: [:index, :show, :edit, :update]}

      posts_controller.rb
      # before_actionにauthenticate_userメソッドを指定してください
      before_action :authenticate_user

      ログインユーザーがアクセスできないページ

      forbid_login_user
      メソッドの作成
      applicationコントローラ内に「ログインユーザーを禁止する」という意味の、forbid_login_userメソッドを作成しましょう。
      このメソッドでは、ログインユーザーが存在する場合、投稿一覧ページにリダイレクトするようにします。メソッドの実行にはbefore_actionを用い、onlyで適用したいアクションを指定しましょう。

      before_action :forbid_login_user,

      application_controller.rb
      # forbid_login_userメソッドを定義してください
      def forbid_login_user
      if @current_user
      flash[:notice] = “すでにログインしています”
      redirect_to(“/posts/index”)
      end
      end

      home_controller.rb
      # before_actionにforbid_login_userメソッドを指定してください
      before_action :forbid_login_user, {only: [:top]}

      users_controller.rb
      # before_actionにforbid_login_userメソッドを指定してください
      before_action :forbid_login_user, {only: [:new, :create, :login_form, :login]}

      ユーザーの編集を制限しよう(ビュー)
      編集ページへのリンクを非表示にしよう
      ユーザー詳細ページで、ログインしているユーザーではない場合には編集ページへのリンクを非表示にしましょう。
      以下の図のように、@userと@current_userのそれぞれのidが等しいかどうかで、表示するか否かを判断します。

      <% if @user.id == @current_user.id %>
      <%= link_to("編集", "/users/#{@user.id}/edit") %>
      <% end %>

      ユーザーの編集を制限しよう(アクション)

      ensure_correct_user
      メソッドの作成
      usersコントローラのeditアクションおよびupdateアクションに制限をかけましょう。「正しいユーザーかを確かめる」という意味のensure_correct_userメソッドを用意し、ログイン中のユーザーのidと編集したいユーザーのidが等しいか判定します。等しくなかった場合はフラッシュを表示し、投稿一覧ページにリダイレクトさせましょう。

      ユーザーの編集を制限しよう(アクション)
      to_iメソッド
      to_i
      ログイン中のユーザーのidは@current_user.idに、編集したいユーザーのidはparams[:id]にそれぞれ代入されています。しかし、params[:id]で取得できる値は文字列であり、数値である@current_user.idと比較してもfalseとなります。to_iメソッドを用いると、文字列を数値に変換することができます。to_iメソッドでparams[:id]を数値に変換し、@current_user.idと比較しましょう。

      # before_actionにensure_correct_userメソッドを指定してください
      before_action :ensure_correct_user, {only: [:edit, :update]}

      # ensure_correct_userを定義してください
      def ensure_correct_user
      if @current_user.id != params[:id].to_i
      flash[:notice] = “権限がありません”
      redirect_to(“/posts/index”)
      end
      end

    シェアする

    • このエントリーをはてなブックマークに追加

    フォローする