【Rails７】deviseでユーザー情報の更新をする方法

# はじめに
RubyOnRailsでdeviseを用いたユーザー管理登録機能のユーザー情報の編集機能の実装についてまとめていきたいと思います。


:::note warn
目的
1. deviseのユーザー管理機能のユーザー情報編集を実装する
1. ユーザー情報を編集する際に、新規登録で登録したパスワードを入力なしで変更できるようにする
1. ユーザー情報を登録した後の遷移先を変更する
:::
この記事の前提として**deviseやdeviseのビューのインストールはすでに導入し、新規登録の実装が終わっている**ものとします。

## 環境
devise (4.9.2)
ruby 3.2.0
Rails 7.0.8

## 実装の流れ
1. deviseのコントローラーをインストール
1. ルーティングを設定
1. ストロングパラメーターを追加（デフォルトのカラム以外を設定している場合のみ）
1. ビューの編集（マイページに遷移するボタンを設定→ユーザー情報編集ページ）
1. コントローラーをカスタマイズ
1. 編集が終了した後の遷移先の変更

## 1.deviseのコントローラーをインストール
~~~:ターミナル
$ rails g devise：controllers users
~~~

上記コマンドを実行すると、`app/controllers/users`以下に６つのDevise用のコントローラーが自動生成されます。

[![コマンド実行後のディレクトリ](https://i.gyazo.com/14779a6704a2e75e6a72db2280e87955.png)](https://gyazo.com/14779a6704a2e75e6a72db2280e87955)

ちなみに生成されたものがどのようなものなのかは、こちらのブログの記事を参考にしてください。
>https://zenn.dev/junki555/articles/2a3aaff6f6a291#%E7%94%9F%E6%88%90%E3%81%95%E3%82%8C%E3%81%9F%E3%82%82%E3%81%AE%E3%81%AE%E8%A7%A3%E8%AA%AC

## 2.ルーティングを設定
```ruby:config/routes.rb
Rails.application.routes.draw do
  devise_for :users, controllers: {
    registrations: "users/registrations",
    sessions: "users/sessions",
    passwords: "users/passwords",
    confirmations: "users/confirmations"
  }
  #　「, controllers:{~省略~}」を追記する
  root to: 'messages#index'
  resources :messages, only: :index
  resources :users, only: [:show]
end
```
`devise_for :users,`の後ろに`controllers:{~省略~}`を追記します。
追記することで、１で生成したコントローラーへのルーティングを設定することができます。

## 3.deviseコントローラーにストロングパラメーターを追加（デフォルトのカラム以外を設定している場合のみ）

```ruby:application_controller.rb
class ApplicationController < ActionController::Base
  before_action :configure_permitted_parameters, if: :devise_controller?


  private

  def configure_permitted_parameters
    devise_parameter_sanitizer.permit(:sign_up, keys: [:name, :profile])
    devise_parameter_sanitizer.permit(:account_update, keys: [:name,　:profile])
    # 上記の記述を追加
  end
end
```
deviseで設定されているカラム以外に追加されている方は、ユーザー情報編集時のストロングパラメーターを追加する必要があります。
```ruby
devise_parameter_sanitizer.permit(:account_update, keys: [:name, :profile])
```
今回の場合、`:name, :profile`を追加する必要があったので上記のように記述しました。
状況に応じて、カラム名を編集する必要があります。

## 4.ビューの編集（マイページに遷移するボタンを設定→ユーザー情報編集ページ）
```html.erb:show.html.erb
<%= link_to "ユーザー情報編集", edit_user_registration_path(current_user.id), class: "header-button" %>
```
マイページにユーザー情報編集ページへのボタンを設定しました。

```html.erb:edit.html.erb
<h2>Edit <%= resource_name.to_s.humanize %></h2>

 # ----------　 下の記述を削除 ----------　

<%= form_for(resource, as: resource_name, url: registration_path(resource_name), html: { method: :put }) do |f| %>
  <%= render "devise/shared/error_messages", resource: resource %>

 # ----------　 上の記述を削除 ----------

 # ----------　 (1)下の記述を追加 ----------　

<%= form_with model: @user, url: registration_path(resource_name), local: true do |f| %>
 <%= render "devise/shared/error_messages", model: f.object  %>
 # ----------　 (1)上の記述を追加 ----------

 # ----------　 (2)下の記述を追加 ----------　

　　　　<div class="field">
　　　　　　<%= f.label :name, "name" %><br />
　　　　　　<%= f.text_field :name, autofocus: true %>
　　　　</div>

 # ----------　 (2)上の記述を追加 ----------


  <div class="field">
    <%= f.label :email %><br />
    <%= f.email_field :email, autofocus: true, autocomplete: "email" %>
  </div>

  <% if devise_mapping.confirmable? && resource.pending_reconfirmation? %>
    <div>Currently waiting confirmation for: <%= resource.unconfirmed_email %></div>
  <% end %>

 # ----------　 (2)下の記述を追加 ----------　

　　　　<div class="field">
　　　　　　<%= f.label :profile, "profile" %>
　　　　　　<%= f.text_field :profile %>
　　　　</div>

 # ----------　 (2)上の記述を追加 ----------

  <div class="field">
    <%= f.label :password %> <i>(leave blank if you don't want to change it)</i><br />
    <%= f.password_field :password, autocomplete: "new-password" %>
    <% if @minimum_password_length %>
      <br />
      <em><%= @minimum_password_length %> characters minimum</em>
    <% end %>
  </div>

  <div class="field">
    <%= f.label :password_confirmation %><br />
    <%= f.password_field :password_confirmation, autocomplete: "new-password" %>
  </div>

 # ----------　 (3)下の記述を削除 ----------　

  <div class="field">
    <%= f.label :current_password %> <i>(we need your current password to confirm your changes)</i><br />
    <%= f.password_field :current_password, autocomplete: "current-password" %>
  </div>

 # ----------　 (3)上の記述を削除 ----------

  <div class="actions">
    <%= f.submit "Update" %>
  </div>
<% end %>

<h3>Cancel my account</h3>

<div>Unhappy? <%= button_to "Cancel my account", registration_path(resource_name), data: { confirm: "Are you sure?", turbo_confirm: "Are you sure?" }, method: :delete %></div>

<%= link_to "Back", :back %>
```
デフォルトの`edit.html.erb`を編集をしました。編集内容は以下のとおりです。
1. `form_for`から`form_with`の記述に変更（`shared/error_messages`の記述も変更しないとエラーが出ます）
1. `:name, :profile`の記入欄を追加
1. ユーザー情報を編集する際に元の設定されたパスワードを入力する欄を削除（次の項目で説明します）

## 5.コントローラーをカスタマイズ
```ruby:registrations_controller.rb
# frozen_string_literal: true

class Users::RegistrationsController < Devise::RegistrationsController
  # before_action :configure_sign_up_params, only: [:create]
  # before_action :configure_account_update_params, only: [:update]

  # GET /resource/sign_up
  # def new
  #   super
  # end
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~　省略　~~~~~~~~~~~~~~~~~~~~~~~~~~~~#
  # The path used after sign up for inactive accounts.
  # def after_inactive_sign_up_path_for(resource)
  #   super(resource)
  # end

 # ----------　 下の記述を追加 ----------
  protected
 
  def update_resource(resource, params)
    resource.update_without_current_password(params)
  end
 # ----------　 上の記述を追加 ----------
end

```
パスワードなしで、パスワードの更新を可能にするには自分でメソッドを定義する必要があります。
`update_without_current_password`を`user.rb`に定義していきます。
詳しくは以下の記事を参考にしてください。
>https://easyramble.com/user-account-update-without-password-on-devise.html

>https://wcf-drill.com/devise%E3%81%AE%E3%83%91%E3%82%B9%E3%83%AF%E3%83%BC%E3%83%89%E6%9B%B4%E6%96%B0%E3%81%8C%E5%87%BA%E6%9D%A5%E3%81%AA%E3%81%84-752.html

~~~ruby:user.rb
class User < ApplicationRecord
  # Include default devise modules. Others available are:
  # :confirmable, :lockable, :timeoutable, :trackable and :omniauthable
  devise :database_authenticatable, :registerable,
         :recoverable, :rememberable, :validatable

        with_options presence: true do
          validates :radio_name, length: { maximum: 7 }
        end
      
        validates :profile, length: { maximum: 140 }

        VALID_PASSWORD_REGEX = /\A(?=.*?[a-z])(?=.*?\d)[a-z\d]+\z/i
        validates :password, format: { with: VALID_PASSWORD_REGEX }

# ----------　 下の記述を追加 ----------

        def update_without_current_password(params, *options)
          if params[:password].blank? && params[:password_confirmation].blank?
            params.delete(:password)
            params.delete(:password_confirmation)
          end
      
          result = update(params, *options)
          clean_up_passwords
          result
        end

# ----------　 上の記述を追加 ----------
end
~~~

上記の記述をすることで、パスワードの更新を実装することができました。

## 6.編集が終了した後の遷移先の変更

deviseのデフォルトの設定では、ユーザー情報を編集したらトップページにリダイレクトされるように設定されています。
その設定を変更するためにコントローラーに記述します。

~~~ruby:
# frozen_string_literal: true

class Users::RegistrationsController < Devise::RegistrationsController
  # before_action :configure_sign_up_params, only: [:create]
  # before_action :configure_account_update_params, only: [:update]

  # GET /resource/sign_up
  # def new
  #   super
  # end
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~　省略　~~~~~~~~~~~~~~~~~~~~~~~~~~~~#
  # The path used after sign up for inactive accounts.
  # def after_inactive_sign_up_path_for(resource)
  #   super(resource)
  # end

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

# ----------　 下の記述を追加 ----------

  def after_update_path_for(resource)
    user_path(current_user.id)
  end
 # ----------　 上の記述を追加 ----------
end
~~~

編集後、ユーザーのマイページに遷移するように設定しました。

## 最後に
deviseのユーザー情報編集機能は今回初めて実装しました。
編集機能についてのブログの記事を頼りに、なんとか実装することができました。
この記事のコードや仕組みの解釈に関して、誤りや改善点などありましたら、是非ご指摘いただけると幸いです。