2
2

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.

[初学者] sorceryでユーザー登録、ログイン・ログアウト機能の作成

Last updated at Posted at 2023-11-20

目的

学んだことを忘れないように備忘録として残す

sorceryとは?

SorceryはRuby on Railsで使用できる認証および認可のGem。これを使用することで、ユーザー認証、パスワードリセット、ユーザー管理などの一般的な認証および認可機能をアプリケーションに簡単に統合できる。

gemとは?

gem は、Rubyプログラミング言語の拡張機能やライブラリをパッケージ化したもの。
RubyGemsを使用することで、他の開発者が書いたRubyライブラリやアプリケーションを簡単にインストールできる。

sorceryの追加

gemファイルにsorceryを追加

gem sorcery

gemの反映

bundle installで追加したgemファイルを反映

bundle install

sorceryのセットアップ

Sorcery gem用の設定ファイルと関連するファイルを生成

必要なファイルをプロジェクトに追加し、設定をカスタマイズできるようにする。

rails g sorcery:install

生成されるファイル

  • config/initializers/sorcery.rb:Sorceryの設定ファイル
  • app/controllers/user_sessions_controller.rb:ユーザーセッション(ログインとログアウト)を管理するためのコントローラー
  • app/models/user.rb:ユーザーモデルのテンプレート。ユーザーに関連するメソッドや属性を追加する場合にこのファイルを編集する。
  • app/views/user_sessions:ログインフォームやログアウトリンクなどのビューファイルが含まれる。
  • db/migrate/*_create_users.rb(実際のファイル名はタイムスタンプによって異なる):ユーザーモデルに関連するデータベーステーブルを作成するマイグレーションファイル

マイグレーションファイルの更新

Sorceryを使ったユーザー認証システムが正しく動作し、データベースと連携できるようにする。

rails db:migrate

マイグレーションファイルの編集

ユーザ登録の際に姓名を入力できるようにしたいため、データベースにlast_name、first_nameカラムをstring型で追加

マイグレーションファイルの作成

rails g migration AddNameToUsers

作成したマイグレーションファイルを編集

class AddNameToUsers < ActiveRecord::Migration
  def change
    add_column :users, :last_name, :string
    add_column :users, :first_name, :string
  end
end

マイグレーションファイルの更新

作成したマイグレーションファイル(AddNameToUsers)に変更を加えたので、更新する。

rails db:migrate

Userモデルのバリデーション設定

app/models/user.rb
Userモデルにバリデーションを追加

class User < ApplicationRecord 
  authenticates_with_sorcery!
 #ユーザーが設定したパスワードの長さが3文字以上であることを確認
  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] } 

 #新規にユーザーが登録された際に、emailが存在するか、登録済みかどうかを確認
  validates :email, uniqueness: true, presence: true
 #新規にユーザーが登録された際に、first_nameが存在するかどうかの確認と文字数の制限
  validates :first_name, presence: true, length: { maximum: 255 }
 #新規にユーザーが登録された際に、last_nameが存在するかどうかの確認と文字数の制限
  validates :last_name, presence: true, length: { maximum: 255 } 
end
#confirmationは、2つの属性の値が一致するかどうかを検証
#presenceは、指定された属性が存在しているかどうかを検証

new_record?: レコードが新しいレコードかどうかを確認するメソッド
changes[:crypted_password]: レコードの crypted_password フィールドに変更があるかどうかを確認

ユーザー登録機能の作成

usersコントローラとビュー生成

rails g controller users new

app/controllers/users_controller.rbとapp/views/users/new.html.erbが生成される。

user_controllerへの記述

class UsersController < ApplicationController

  def new; end

  def create
  # @userにuser_paramsに入ったデータを代入
    @user = User.new(user_params)
    if @user.save
    #login_path ヘルパーメソッドで、保存が成功したらログインページへ飛ばす
      redirect_to login_path
    else
      #保存が失敗したら登録画面へ飛ばす
      render 'new'
    end
  end

  private
 #ユーザー登録画面からUserモデルへ送信されたパラメーターを要求し、その中からpermitで指定したパラメータを取得
  def user_params
     params.require(:user).permit(:last_name, :first_name, :email, :password, 
:password_confirmation)
  end
end

ユーザーの新規登録ページの作成

new.html.erbへの記述
form_withで投稿フォームを作成するときは、form.htmlタグ名 :カラム名とする。 (カラム名は保存される先のテーブルのカラム名を指定)
上記の場合は、登録ボタンが押されたら、Userモデルの各カラムに入力内容が保存されるようする。

<div class="container">
  <div class="row">
    <div class="col-md-10 col-lg-8 mx-auto">
      <h1>ユーザー登録</h1>
      <%= form_with model: @user do |f| %>
        <div class="mb-3">
          # ユーザーがこのフォーム内の last_name テキストフィールドに入力した値が、
          # フォームの送信時に last_name という名前のパラメーターとしてサーバーに送信される
          <%= f.label :last_name, class: "form-label" %>
          <%= f.text_field :last_name, class: "form-control" %>
        </div>
        <div class="mb-3">
          <%= f.label :first_name, class: "form-label" %>
          <%= f.text_field :first_name, class: "form-control" %>
        </div>
        <div class="mb-3">
          <%= f.label :email, class: "form-label" %>
          <%= f.email_field :email, class: "form-control" %>
        </div>
        <div class="mb-3">
          <%= f.label :password, class: "form-label" %>
          <%= f.password_field :password, class: "form-control" %>
        </div>
        <div class="mb-3">
          <%= f.label :password_confirmation, class: "form-label" %>
          <%= 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>

ルートの設定

config/routes.rb

Rails.application.routes.draw do
 #ルートURLにアクセスされた場合、static_pages コントローラーの top アクションを呼び出す
  root 'static_pages#top' 

 #/login にアクセスされた場合、user_sessions コントローラーの new アクションを呼び出す
  get 'login', to: 'user_sessions#new'
 #/login に対してHTTP POSTリクエストが送信された場合、user_sessions コントローラーの create アクションが呼び出す
  post 'login', to: 'user_sessions#create'
 #/logout に対してHTTP DELETEリクエストが送信された場合、user_sessions コントローラーの destroy アクションが呼び出す
  delete 'logout', to: 'user_sessions#destroy' 

 #新規ユーザーの作成画面を表示する new アクションと、ユーザーの作成を行うcreateアクションを提供するためのURLが自動的に生成される
  resources :users, only: %i[new create]
end

ログイン機能の作成

user_sessionsコントローラを生成します

rails g controller user_sessions

app/controllers/user_sessions_controller.rbが生成される。
UserSessionController にログインの処理を記述
app/controllers/user_sessions_controller.rb

class UserSessionsController < ApplicationController
  # require_loginフィルターを new と create アクションに適用しないように指示
   # 新しいユーザーを作成するための new アクションと create アクションでは、ログインが必要ないことを示す
   skip_before_action :require_login, only: %i[new create]

   def new; end

   def create
     @user = login(params[:email], params[:password])
     if @user
       # ログインに成功した場合、root_pathにリダイレクト
       redirect_to root_path
     else
       # ログインに失敗した場合、ログインページにリダイレクト
       render :new
     end
   end
end

skip_before_action :require_login, only: %i[new create]を設定するのは、ログインを行う前にログインページにアクセスできて、ログインしていない状態でcreateアクションを実行する必要があるため。

ヘッダーの作成

ログイン前のヘッダーを作成して、ログイン・未ログイン状態によって表示を変えれるようにする。
まずは、ログイン・未ログイン状態でヘッダーが切り替わるように設定。
app/views/layouts/application.html.erb

<body>
  <% if logged_in? %>
    <%= render 'shared/header' %>
  <% else %>
    <%= render 'shared/before_login_header' %>
  <% end %>
  <%= yield %>
  <%= render 'shared/footer' %>
</body>

未ログイン時のヘッダーを作成
app/views/shared/_before_login_header.html.erb

<header>
  # ナビゲーションバーを定義するためのHTML要素を生成
  <nav class="navbar navbar-expand-lg navigation navbar-light bg-light">

    # ロゴ画像を表示するためのコード
    <%= link_to root_path, class: "navbar-brand" do %>
      <%= image_tag "#" %> # 任意の画像を入れる
    <% end %>  

    # Bootstrapのナビゲーションバーでハンバーガーメニューを定義するためのHTML要素を生成
    <button class='navbar-toggler' data-bs-toggle='collapse' data-bs-target='#navbarSupportedContent' aria-controls='navbarSupportedContent' aria-expanded='false' aria-label='Toggle navigation'>
      <span class="navbar-toggler-icon"></span>
    </button>
    
    # ナビゲーションバーで折りたたみメニューを定義するためのHTML要素を生成
    <div class="collapse navbar-collapse" id="navbarSupportedContent">
      # ナビゲーションバーにメニュー項目を追加するためのHTML要素を生成
      <ul class="navbar-nav ms-auto main-nav align-items-center">
        #  ログインというテキストにリンクを作成
        <li class="nav-item">
          <%= link_to 'ログイン', login_path, class: "nav-link" %>
        </li>
      </ul>
    </div>
  </nav>
</header>

ログイン時のヘッダーを作成
app/views/shared/_login_header.html.erb

<header>
  <nav class="navbar navbar-expand-lg navigation navbar-light bg-light">
      <%= link_to root_path, class: "navbar-brand" do %>
        <%= image_tag "#" %> # 任意の画像を入れる
      <% end %>
      # Bootstrapを使用してナビゲーションバーのトグル(折り畳み)ボタンを作成
      <button class='navbar-toggler' data-bs-toggle='collapse' data-bs-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 ms-auto main-nav align-items-center'>
       <li class="nav-item">
             # ユーザーがログアウトを実行した際には、UserSessionsController の destroy アクションが呼び出され、logout メソッドが実行される
              <%= link_to 'ログアウト', logout_path, class: 'dropdown-item' %>
           </li>
        </ul>
      </div>
  </nav>
</header>

ログアウト機能の作成

UserSessionController にログアウトの処理を記述
app/controllers/user_sessions_controller.rb

class UserSessionsController < ApplicationController
  # require_loginフィルターを new と create アクションに適用しないように指示
  # 新しいユーザーを作成するための new アクションと create アクションでは、ログインが必要ないことを示す
  skip_before_action :require_login, only: %i[new create]

  def new; end

  def create
     @user = login(params[:email], params[:password])
     if @user
       # ログインに成功した場合、root_path(トップページ)にリダイレクト
       redirect_to root_path
     else
       # ログインに失敗した場合、ログインページにリダイレクト
       render :new
     end
   end
 
  def destroy
      # logoutメソッドを呼び出して、セッションをクリアする
      logout
      # ログアウトに成功した場合、root_path(トップページ)にリダイレクト
    redirect_to root_path
  end
end
2
2
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
2
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?