3
5

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 3 years have passed since last update.

Railsでdeviseを使用してプロフィール編集ページとは別でパスワード編集ページを自作する方法

Posted at

記事の目的

  • Railsでdeviseでプロフィール編集ページとは別でパスワード編集ページを自作した時に、同じようなことをしている記事が見つからず実装に手間取ったので、備忘録として残す
  • 今後同じことをしたい人が現れた時に、少しでも参考になるように
  • まだまだ勉強中の身です。もし間違っている箇所があれば教えてください🙇‍♂️

前提条件

  • Rails 6.0.2.2
  • Ruby 2.7.0
  • deviseを導入している

実現したかったこと

  • deciseでデフォルトで設定されているregistration#editregistration#updateでは名前、メールアドレスなどのプロフィール情報を編集する
  • プロフィール編集時はパスワード入力なしで編集できるようにする
  • 上記とは別でパスワード編集用のページを自作する
  • 現在のパスワード、新しい用パスワード、確認用パスワードを入力するフィールドがあり、入力内容に不備があればパスワード編集ページにリダイレクトし、エラーを出す

やったこと

  • ルーティングの設定
  • 対応するviewの作成
  • 実現したい内容にあわせてregistration_controllerでdeviseをオーバーライド

まずはroures.rbdevise_scopeでルーティングを設定

edit_passwordupdate_password どちらも書きます。
deviseはなぜかデフォで user/edit でアクセスできてしまう(:idがない)が、それだと気持ち悪いので自作したページにはちゃんと:idを足します。

roures.rb
  devise_scope :user do
    get 'registrations/:id/edit_password', to: 'registrations#edit_password', as: 'edit_password'
    put 'registrations/:id/update_password', to: 'registrations#update_password', as: 'update_password'
  end

パスワード編集ページの作成

パスワード編集用のviewを作成 。
中身はデフォで作られているプロフィール編集ページのものをベースにして書き換えればOKです。
form_forタグのurlに、↑で設定した更新時に使用するアクション(今回だとupdate_password)のpathを指定します。

edit_password.html.slim
.password-edit
  h2 パスワード編集
  = form_for(resource, as: resource_name, url: update_password_path, html: { method: :put }) do |f|
    = render 'devise/shared/error_messages', resource: resource
    .form
      .field
        = f.label :current_password, '現在のパスワード'
        br
        = f.password_field :current_password, autocomplete: 'current-password', class: 'input-box'
      .field
        = f.label :password, '新しいパスワード'
        br
        = f.password_field :password, autocomplete: 'new-password', class: 'input-box', placeholder: '6文字以上'
      .field
        = f.label :password_confirmation, '新しいパスワード(確認)'
        br
        = f.password_field :password_confirmation, autocomplete: 'new-password', class: 'input-box', placeholder: '6文字以上'
      .actions
        = f.submit '更新する', class: 'submit-box'

この状態ではまだローカルからこのviewにアクセスできないと思います。(ArgumentErrorが出る)
理由は、edit_passwordは自作したもののためdeviseをうまく経由できておらず、resourceがviewに渡されていないから。
この問題をdeviseの内容をオーバーライドすることで解決していきます。

registration_controllerdeviseをオーバーライドしていく(ここが大変だった..!)

まずは↑の状態を解消するために、devisebefore_actionをオーバーライドしていきます。
devise本体のコードは以下。

devise/registrations_controller.rb
prepend_before_action :authenticate_scope!, only: [:edit, :update, :destroy] 
prepend_before_action :set_minimum_password_length, only: [:new, :edit]

詳細は本体のコードを読んで欲しいですが、このauthenticate_scope!が現在のresourceを渡すアクションのため、ここにedit_passwordupdate_passwordを追加します
(その下はパスワードの最小文字数をセットするもの。ついでに追加します)

registration_controller.rb
class RegistrationsController < Devise::RegistrationsController
  prepend_before_action :authenticate_scope!, only: [:edit, :edit_password, :update, :update_password, :destroy]
  prepend_before_action :set_minimum_password_length, only: [:new, :edit, :edit_password]

これで無事にパスワード編集ページにアクセスできるようになりました!
次はupdate_passwordの条件を書いていきます。
devise本体のupdateをベースにすればOKですが、そのままだと更新失敗時にプロフィール編集ページにリダイレクトされてしまうため、パスワード編集ページにリダイレクトされるように書き換えます。

registrations_controller.rb

def edit_password; end

def update
    self.resource = resource_class.to_adapter.get!(send(:"current_#{resource_name}").to_key)
    prev_unconfirmed_email = resource.unconfirmed_email if resource.respond_to?(:unconfirmed_email)

    resource_updated = update_resource(resource, account_update_params)
    yield resource if block_given?
    if resource_updated
      set_flash_message_for_update(resource, prev_unconfirmed_email)
      bypass_sign_in resource, scope: resource_name if sign_in_after_change_password?

      respond_with resource, location: after_update_path_for(resource)
    else
      clean_up_passwords resource
      set_minimum_password_length
      #この1行を書き換えた
      render 'edit_password'
    end
  end

次に、実際に更新を行っている update_resourceアクションを条件に合わせてオーバーライドします。
デフォルトはこんな感じ。

devise/registrations_controller.rb
  def update_resource(resource, params)
    resource.update_with_password(params)
  end

ここでやりたいことを改めて振り返ると、プロフィールを編集する時はパスワードなしで更新できるようにし、パスワードを編集する時だけパスワード入力を求めるようにしたいです。
しかしこのままだとプロフィールを編集する時もパスワード入力を求められてしまうので、以下のようにオーバーライドします。

registrations_controller.rb
  protected

  def update_resource(resource, params)
    if params[:password].blank? && params[:password_confirmation].blank? && params[:current_password].blank?
      resource.update_without_password(params)
    else
      resource.update_with_password(params)
    end
  end

これでちゃんと動くようになった!
最後に、無事に更新できた時のリダイレクト先を設定します。
デフォルトはroot_pathになっていますが、ここをユーザー詳細ページにしたいため、after_update_path_for をオーバーライドします。

registrations_controller.rb
  protected

  def after_update_path_for(resource)
    user_path(current_user)
  end

以上!

まとめ

gemはとても便利ですが、カスタマイズしたい時は、本体のコードをよく理解する必要があって大変だなと痛感..!!
でも無事に解決できて良かったです。最後までご覧いただきありがとうございました。

3
5
6

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
3
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?