必要事項を記載して更新しようとすると、以下のようなことが起きた。
- current_passwordを正しく入力しても不正とみなされる
- emailアドレスを変更していない場合でも既に使用されているとしてバリデーションに引っかかる
上記を解消したコードは以下の通り(※備忘録なので詳細説明省く)
app/controllers/staffs/registrations_controller.rb
# frozen_string_literal: true
class Staffs::RegistrationsController < Devise::RegistrationsController
before_action :set_staff, only: %i(edit update destroy)
before_action :authenticate_staff!, only: %i(edit update destroy)
load_and_authorize_resource :class => 'Staff', except: %i(create update)
before_action :configure_permitted_parameters, if: :devise_controller?
def update
authorize! :update, @staff
if current_staff || (current_company && @staff.company_id == current_company.id)
params[:staff][:service_ids].each do |service_id|
service = Service.find(service_id)
fee = params[:staff][:service_fees][service_id].presence || service.default_fee
duration = params[:staff][:service_durations][service_id].presence || service.default_duration
# serviceとのアソシエーション部分のみ先に保存
@staff.update_or_create_available_service(service_id: service_id, fee: fee, duration: duration)
end
# ここから
if current_staff
self.resource = resource_class.to_adapter.get!(send(:"current_#{resource_name}").to_key)
bypass_sign_in resource, scope: resource_name if sign_in_after_change_password?
elsif @staff.company_id == current_company.id
self.resource = resource_class.to_adapter.get!(@staff.to_key)
end
# ここまで追記
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)
respond_with resource, location: after_update_path_for(resource)
else
clean_up_passwords resource
set_minimum_password_length
respond_with resource
end
else
redirect_to root_path, alert: 'アクセス権限がありません。'
end
end
protected
def authenticate_staff!(opts = {})
if action_name == 'update'
@staff = Staff.new(staff_params)
elsif !@staff
@staff = Staff.find(params[:id])
end
unless staff_signed_in? || (company_signed_in? && @staff.company_id == current_company.id)
redirect_to new_company_session_path, alert: 'アクセス権限がありません。'
end
end
def set_staff
begin
if resource.present?
@staff = Staff.find(resource.id)
elsif params[:id]
@staff = Staff.find(params[:id])
end
rescue ActiveRecord::RecordNotFound
redirect_to root_path, alert: '指定されたスタッフが見つかりません。'
end
end
def set_company
@company = @staff.company
end
def configure_permitted_parameters
devise_parameter_sanitizer.permit(:sign_up, keys: [:id,
:company_id,
:name,
:email,
:position,
:telephone,
:postal_code,
:prefecture,
:city,
:street_address,
:description,
:password,
:password_confirmation,
:current_password,
service_ids: []])
devise_parameter_sanitizer.permit(:account_update, keys: [:id,
:company_id,
:name,
:email,
:position,
:telephone,
:postal_code,
:prefecture,
:city,
:street_address,
:description,
:password,
:password_confirmation,
:current_password, # ここを追記
service_ids: []
])
end
def after_sign_up_path_for(resource)
staff_path(resource)
end
def after_update_path_for(resource)
staff_path(resource)
end
private
def staff_params
permitted_keys = [:id,
:company_id,
:name,
:position,
:telephone,
:postal_code,
:prefecture,
:city,
:street_address,
:description,
:password,
:password_confirmation,
:current_password,
service_ids: [],
]
# Emailの更新が必要な場合のみ `email` を追加
permitted_keys << :email if email_needs_update?
params.require(:staff).permit(permitted_keys)
end
def email_needs_update?
params[:staff][:email].present? && params[:staff][:email] != Staff.find(params[:staff][:id]).email
end
def staff_signed_in?
!current_staff.nil?
end
# 以下のメソッドをオーバーライド
def update_resource(resource, params)
if params[:password].blank? && params[:password_confirmation].blank?
params.delete(:password)
params.delete(:password_confirmation)
resource.update(params.except(:current_password))
else
resource.update_with_password(params)
end
end
# 以下のメソッドをオーバーライド
def email_needs_update?
params[:staff][:email].present? && params[:staff][:email] != Staff.find(params[:staff][:id]).email
end
end
app/models/staff.rb
class Staff < ApplicationRecord
include PrefectureEnum
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :validatable
has_many :reservations
has_many :schedules, dependent: :destroy
has_many :staff_business_hours, dependent: :destroy
has_many :staff_replies, dependent: :destroy
has_many :staff_reviews, dependent: :destroy
has_many :available_services, dependent: :destroy
has_many :services, through: :available_services
has_many :time_tables, dependent: :destroy
has_many :staffs, dependent: :destroy
belongs_to :company
# ここから
attr_accessor :current_password
validates :email, uniqueness: { case_sensitive: false }, if: :email_changed?
validate :check_current_password, if: :password_present?
# ここまで追記
validates :name, presence: true
enum position: { employee: 0, manager: 1, director: 2 }
def update_or_create_available_service(service_id:, fee:, duration:)
available_service = self.available_services.find_or_initialize_by(service_id: service_id)
available_service.fee = fee
available_service.duration = duration
available_service.save
end
private
# 追記。オーバーライド
def password_present?
self.password.present? || self.password_confirmation.present?
end
# 追記。オーバーライド
def check_current_password
unless Devise::Encryptor.compare(self.class, self.encrypted_password, current_password)
errors.add(:current_password, 'is incorrect')
end
end
# 追記。オーバーライド
def email_must_be_unique
# データベースに同じemailが存在するかどうかをチェック
if Staff.where.not(id: self.id).exists?(email: self.email)
errors.add(:email, "は既に使用されています")
end
end
end