Rails Deviseでユーザー編集をパスワードを入力しないで更新する方法

#Rails Deviseでユーザー編集をパスワードを入力しないで更新する方法

Deviseで現在のパスワードを入力せずにユーザ情報を更新する方法をまとめます。

# 目次
* [動作環境](#動作環境)
* [前提条件](#前提条件)
* [手順概略](#手順概略)
* [詳細手順](#詳細手順)
  * [STEP1.新規登録ためのストロングパラメータの設定](#新規登録のためのストロングパラメータの設定)
  * [STEP2.アップデートのためのストロングパラメータの設定](#アップデートのストロングパラメータの設定)
  * [STEP3.パスワード無しで更新するためのメソッドを定義](#パスワード無しで更新するためのメソッドを定義)
  * [STEP4.Viewファイルからcurrent_passwordフィールドの削除](#viewファイルからcurrent_passwordフィールドの削除)
* [結果](#結果)

## 動作環境
`OS : macOS Mojave 10.14.6`
`ruby : 2.6.5p114`
`rails : 5.2.4`
`devise : 4.7.1`

# 前提条件

すでにgemのインストールからviewの作成までの手順が終わっていると仮定します。

1. devise gemのインストール済
2. rails generate devise install済
3. rails generate devise:views済
4. usersテーブルにnameなどのデフォルト以外のカラムが追加済

# 手順概略

[STEP1. 新規登録のためのストロングパラメータを`application_controller`に追加]
(#新規登録のためのストロングパラメータの設定)

[STEP2. `registrations_controller.rb`を`controllers/users/`に作成し,更新するためのストロングパラメータを追加, ルーティングを修正](#アップデートのストロングパラメータの設定)

[STEP3. パスワード無しでアップデートするためのメソッドを`registrations_controller.rb`と`user.rb`に記載](#パスワード無しで更新するためのメソッドを定義)

[STEP4. Viewからcurrent_passwordフィールドを削除する](#viewファイルからcurrent_passwordフィールドの削除)

# 詳細手順

## 新規登録のためのストロングパラメータの設定

現状では後から追加したnameのパラメータはstrongパラメータではじかれてしまうため
application_controllerに以下のコードを記載します.

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

  protected

  def configure_permitted_parameters
    devise_parameter_sanitizer.permit(:sign_up, keys: [:name])
  end
end
```
コンソールで確認するとnameパラメータを受け取りユーザを作成できています.

```bash
irb(main):001:0> User.create(name: 'abc' , email:'abc@example.com',password:'123456')
   (1.3ms)  COMMIT
=> #<User id: 2, email: "abc@example.com", created_at: "2020-05-30 10:41:46", updated_at: "2020-05-30 10:41:46", name: "abc">
```

次はviewにnameの入力フィールドを記載します.

```html:new.html.erb
<h2>Sign up</h2>

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

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

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

<div class="field">
  <%= f.label :password %>
  <% if @minimum_password_length %>
  <em>(<%= @minimum_password_length %> characters minimum)</em>
  <% end %><br />
  <%= f.password_field :password, autocomplete: "new-password" %>
</div>

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

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

<%= render "devise/shared/links" %>
```
これでuserの新規登録は完了です。

## アップデートのストロングパラメータの設定

次にユーザ編集用のviewにもnameフィールドを追加します.

```html:edit.html.erb
//追加
<div class="field">
  <%= f.label :name %><br />
  <%= f.text_field :name, autofocus: true, autocomplete: "name" %>
</div>
```
ここでアップデートボタンを押してもnameはupdateされないことがわかります.

そこでnameカラムをアップデートするために`users/registrations_controller.rb`を作成し,以下のように記載します.

```ruby:registrations_controller.rb
class Users::RegistrationsController < Devise::RegistrationsController
  before_action :configure_account_update_params, only: [:update]

  protected

  def configure_account_update_params
    devise_parameter_sanitizer.permit(:account_update, keys: [:name])
  end
end
```
そして、この`registartions_controller`を参照するためにルーティングを修正します.

```ruby:routes.rb
Rails.application.routes.draw do
  root 'blogs#index'
  #変更箇所
  devise_for :users, controllers: {
    registrations: 'users/registrations'
  }
  resources :blogs
  end
```
するとUsersのnameカラムがアップデートできるようになります.

## パスワード無しで更新するためのメソッドを定義

ただ,現時点ではcurrent_passwordを入力しないとupdateの際にエラーになります.
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/526570/443a8dff-1a66-115a-7ca0-04367a582175.png)

そこで, まずはuserモデルにパスワード無しでアップデートするメソッドを定義します.

```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

  //追加するメソッド
  def update_without_current_password(params, *options)
    params.delete(:current_password)

    if params[:password].blank? && params[:password_confirmation].blank?
      params.delete(:password)
      params.delete(:password_confirmation)
    end

    result = update_attributes(params, *options)
    clean_up_passwords
    result
  end
end
```

その後,`registrations_controller`から`update_without_password`を呼び出します.

```ruby:registrations_controller.rb
class Users::RegistrationsController < Devise::RegistrationsController
  before_action :configure_account_update_params, only: [:update]

  protected
  //追加(必須)
  def update_resource(resource, params)
    resource.update_without_password(params)
  end

  //必須ではないがupdate後にtop画面にリダイレクトするメソッド
  def after_update_path_for(_resource)
    blogs_path
  end

  def configure_account_update_params
    devise_parameter_sanitizer.permit(:account_update, keys: [:name])
  end
end
```

## viewファイルからcurrent_passwordフィールドの削除

viewファイルから`current_password`を削除します.

```html:edit.html.erb
//削除
<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>

```
## 結果

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/526570/5862f650-06ed-26b7-8b35-a48300f0e4c8.png)

エラーが出ずにuser nameがアップデートされていることがわかります.

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/526570/3a6423dc-9883-e26a-a609-696dcb13adc4.png)