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.

【Rails】Deviseをカスタマイズして使いたい!!(コントローラ、ビュー編)

Posted at

どうした?

前回まででモデルとルーティングを設定し、コントローラとビューファイルを作成しました。
今回はコントローラとビューファイルを作り込んで、

  • ユーザー作成
  • ユーザー編集
  • ログイン
  • ログアウト

ができるようにします。

今回の目標

ユーザー情報を操作するため、users/resistrations_controller

  • new
  • create
  • edit
  • update

アクションを実装します。

ログイン情報を操作するため、users/sessions_controller

  • new
  • create
  • delete

アクションを実装します。

手順

ユーザー作成

ビューファイルの作成

まずはユーザー情報を入力するビューファイルを作成していきましょう。
ユーザーの作成と編集は同じレイアウトなので、部分テンプレートを使用します。
views/users/resistrationsフォルダ内に_user_form.html.erbファイルを作成します。

前回作成した空のnew.html.erbファイルで部分テンプレートを呼び出します。

<%= render 'user_form' %>

_user_form.html.erbを以下のように編集します。

<%= form_with model: @user do |f| %>
  <div class="my-3">
    <%= f.label :name, class: 'form-label fw-bold' %>
    <%= f.text_field :name, class: 'container-fluid' %>
  </div>
  <div class="my-3">
    <%= f.label :email, class: 'form-label fw-bold' %>
    <%= f.text_field :email, class: 'container-fluid' %>
  </div>
  <div class="my-3">
    <%= f.label :password, class: 'form-label fw-bold' %>
    <%= f.password_field :password, class: 'container-fluid' %>
  </div>
  <div class="my-3">
    <%= f.label :password_confirmation, class: 'form-label fw-bold' %>
    <%= f.password_field :password_confirmation, class: 'container-fluid' %>
  </div>
  <div class="my-3">
    <%= f.submit '登録', class: "btn btn-primary" %>
  </div>
<% end %>

コントローラの作成

Deviseで自動作成したコントローラには基本的なCRUDアクションといくつかのメソッドが定義されています。
まずはアクションの中身を見てみましょう。

# GET /resource/sign_up
def new
  super
end

全てのアクションがsuper、つまり親クラスであるDevise::RegistrationsControllerを継承していますね。
この親クラスを見てみると、、

うーん、、、分かるようで分からないような、、、
何だかあちこちから変数を引っ張って来て便利なようにしてくれている風ですが、僕がしたいのはもっとシンプルなものです。
newアクションはギリギリ理解できたとしても、createupdateなどを完全に乗りこなせる自信がありません。
なので、superは消してしまいましょう。
僕がnewアクションでしたいことは、ただ1つ。これだけです。

def new
  @user = User.new
end

コントローラ内のprotected以下を見てみると、いくつかのメソッドが定義されています。
これらはストロングパラメータの設定に必要なようです。
モデルを作成したとき、Deviseが初めから用意していたカラムを思い出してください。
emailとpassword関連ですね。
それ以外のカラムをストロングパラメータで送信する(今回はname)場合にkeys: [:attribute]の部分を編集するようです。
、、、う〜ん、どこかでエラーが起きそう。
なので消します。

結局、コントローラは以下のようになりました。

class Users::RegistrationsController < Devise::RegistrationsController
  # before_actionも削除

  def new
    @user = User.new
  end

  def create
    @user = User.new(create_users_params)
    if @user.save
      sign_in(@user)
      redirect_to # 任意のパス
    else
      render 'new'
    end
  end

# edit, updateは後述

  private

  def create_users_params
    params.require(:user).permit(:name, :email, :password)
  end
end

Deviseに慣れたプロフェッショナルはsuperやメソッドを使いこなすのかもしれませんが、現在の僕はこれで精一杯です。

createアクション内でsign_inメソッドを使用しています。
これはDeviseで用意されているメソッドで、認証済のユーザーをログインさせるのに便利です。
sign_in(@user)を与えることで@userに対して

  • ログイン
  • セッションの保持
  • /sign_up, /loginへのアクセス不可

をしてくれます。

試しに、画面上でサンプルユーザーを1人登録してみましょう。
登録後、コンソールでユーザーが増えていることが確認できたら実装成功です!

ユーザー編集

続いて、ユーザー情報を編集できるようにします。

ビューファイルの作成

edit.html.erbファイルにて先程作成した部分テンプレートを呼び出します。

<%= render 'user_form' %>

コントローラの設定

まずは、アクション内でやりたいことを整理します。

editアクション
newと同じように、superを削除し、現在のユーザーを取得します。

updateアクション
editと同じように、superを削除し、現在のユーザーを取得します。
現在のユーザーに対してupdateメソッドに渡されたparams情報で更新するようにします。

ユーザー情報の編集では、パスワードの入力を要求しないようにします。
Deviseはデフォルトでパスワードを更新するとログアウトする仕様になっています。
これをカスタマイズするために

  • update_resourceメソッドをオーバーライドする
  • Userモデルにupdate_without_current_passwordを定義する
  • update_users_paramsをストロングパラメータに設定する

という手順をとります。

update_resourceメソッドをオーバーライドする

Deviseはデフォルトでupdate_resourceというメソッドを持ち合わせています。
このメソッドはユーザー情報の更新の際にパスワードを更新するとログアウトする仕様となっています。
そのため、コントローラにてメソッドをオーバーライドする必要があります。

users/resistrations_controller.rb
private

def update_resource(resource, params)
  resource.update_without_current_password(params)
end

Userモデルにupdate_without_current_passwordを定義する

上でオーバーライドしたコードの中でupdate_without_current_passwordを使用しているので、これをUserモデルで定義します。

user.rb
def update_without_current_password(params)
  if params[:password].blank? && params[:password_confirmation].blank?
    params.delete(:password)
    params.delete(:password_confirmation)
  end
  update(params)
end

if文内ではpasswordpassword_confirmationの両方が空欄のとき、送信するparamsハッシュからそれぞれの存在を削除(nil)しています。
passwordpassword_confirmationを空欄(blank)ではなく、そもそもそんなキーはない(nil)ことにしてしまうという手法です。
(blankだとバリデーションエラーになるが、nilならストロングパラメータに引っかからない限りparamsとして通すイメージ)

update_users_paramsをストロングパラメータに設定する

createupdateでは送信するカラムが異なるため、それぞれに対してストロングパラメータを定義します。
update_users_paramsは以下のようになります。

def update_users_params
  params.require(:user).permit(:name, :email)
end

以上をまとめると、ユーザー情報の更新は以下のようになります。

users/resistrations_controller.rb
def edit
  @user = User.find(params[:id])
end

def update
  @user = User.find(params[:id])
  if @user.update(update_users_params)
    redirect_to categories_path, notice: "ユーザー「#{@user.name}」を更新しました。"
  else
    render 'edit'
  end
end

private

# create_users_paramsは先述

def update_users_params
  params.require(:user).permit(:name, :email)
end

def update_resource(resource, params)
  resource.update_without_current_password(params) # update_without_current_passwordはUserモデルで定義
end

ログイン

続いて、ログイン機能です。

ビューファイルの作成

views/users/sessionsフォルダ内にnew.html.erbファイルを以下のように作成しました。

<%= form_with url: login_path, scope: :session do |f| %>
  <div class="my-3">
    <%= f.label :email, class: 'form-label fw-bold' %>
    <%= f.text_field :email, class: 'container-fluid' %>
  </div>
  <div class="my-3">
    <%= f.label :password, class: 'form-label fw-bold' %>
    <%= f.password_field :password, class: 'container-fluid' %>
  </div>
  <div class="my-3">
    <%= f.submit 'ログイン', class: "btn btn-primary" %>
  </div>
<% end %>

コントローラの作成

users/sessions_controller#createでは、ユーザー作成時に使用したsign_inメソッドを使用します。

users/sessions_controller.rb
def new
end

def create
  @user = User.find_by(email: params[:session][:email])
  if @user.valid_password?(params[:session][:password])
    sign_in(@user)
    redirect_to # 任意のパス
  else
    render 'new', notice: "ログインに失敗しました"
  end
end

ここでは、まず入力されたemailを持つユーザーを探し、
valid_password?というDevise標準のメソッドを使用してユーザーが持つパスワードと入力されたパスワード情報が一致するかを調べています。
emailpasswordが一致したらsign_inメソッドでユーザーをログインさせます。

ログイン機能はこれで実装完了です。

ログアウト

ログアウトリンクの作成

ログアウトリンクは以下のように作成しました。

<%= link_to('ログアウト', logout_path, method: :delete) if user_signed_in? %>

ここでは、user_signed_in?というメソッドを使用して条件分岐させています。
このメソッドはDeviseで標準で用意されているメソッドで、セッション情報にユーザーがいるかどうかを調べます。
ちなみにDeviseはcurrent_userも標準で用意しており、current_user.nameなども使用可能です。

コントローラの作成

ログアウト機能は、セッションから現在のユーザーを削除する機能です。
これはDeviseでも同様におこなっているので、superを利用してしまいましょう。

def destroy
  super
end

ログアウト機能はこれで実装完了です。

まとめ

以上で、Deviseをカスタマイズして使いたい!!シリーズを完結したいと思います。
Deviseは便利(に思える)一方、中身を分からずに使用する恐怖感もあると思います。
本記事が少しでもDeviseの導入の参考になれば嬉しいです!

参考

スペシャルサンクス

本記事を書きながら

  • Deviseのカスタマイズなんて需要あるんかな、、?
  • スクールに通ってる人はみんな勉強済みなのでは、、?

と思っていた僕に、「未経験でソースコードを深堀りするなんて凄いね」と言って下さった現役エンジニアの一言でやる気が起きました。
ありがとうございました!

また、users/resistrations_controller#updateの実装に沼った際にQiitaの質問記事で沢山の方にお助けいただきました。
ありがとうございました!

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?