RailsのDeviseでユーザーの論理削除を実装
背景
- 物理削除だとユーザーの動向が追えず心配
- ユーザー退会後の同じメールアドレスで登録できるようにしたい
サンプルリポジトリ
こちらから、実装済みソースコードが見られます。
注意
サンプルのリポジトリは、Dockerで起動することを前提に作成されています。
Dockerを使用しない場合は、appフォルダ以下で通常のrailsの起動処理を行ってください。
なお、記事ではDockerを使用せず導入する手順で記載していますのでご注意ください。
Rails6で記述していますが、Rails5でも同様に動作すると思います。
ソースコードのコメントについて
基本的にコメントを多めに記載しています。こちらを参考に実装して、ロジックの理解を深めてください。
Seedファイルにユーザーの初期情報が入っています。ユーザーの作成が面倒な場合にご利用ください。
作業
# Deviseのインストール
rails g devise:install
# DeviseのUserモデルを作成
rails g devise User
# コントローラーをオーバーライドするため必要
bundle exec rails g devise:controllers users
# ビューのカスタマイズ用のため(今回は未使用)
bundle exec rails g devise:views users
# この後使うカラムをusersテーブルに追加
rails g migration AddDeletedAtColumnToUsers deleted_at:datetime --table users
# マイグレーションの実行
rails db:migrate
# とりあえずhomesで色々とするのでコントローラー作成
rails g controller homes index
ルーティングをオーバーライドするコントローラーに向ける
Rails.application.routes.draw do
root to: 'homes#index'
devise_for :users, controllers: {
registrations: 'users/registrations',
sessions: 'users/sessions'
}
end
とりあえず、の暫定トップページを作成
<h1>Homes#index</h1>
<p>Find me in app/views/homes/index.html.erb</p>
<ul>
<li>
<%= link_to '新規登録', new_user_registration_path %>
</li>
<li>
<%= link_to 'ログイン', new_user_session_path %>
</li>
<%# ログイン済みならログアウト・退会リンクを表示 %>
<% if user_signed_in? %>
<li>
<%= link_to 'ログアウト', destroy_user_session_path, method: :delete %>
</li>
<li>
<%= link_to '退会', user_registration_path, method: :delete, data: { confirm: "アカウントを削除してもよろしいですか?" } %>
</li>
<% end %>
</ul>
退会処理をオーバーライド
退会後も同じメールアドレスで登録できるようにsoft_deleteで変更しているところがポイント
# 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
# POST /resource
# def create
# super
# end
# GET /resource/edit
# def edit
# super
# end
# PUT /resource
# def update
# super
# end
# DELETE /resource
def destroy
# 論理削除処理
soft_delete(current_user)
# Deviceの論理削除後の後処理
respond_with_navigational do
# 強制ログアウト
sign_out current_user
# ログアウト後のページ遷移
redirect_to root_path
end
end
# GET /resource/cancel
# Forces the session data which is usually expired after sign
# in to be expired now. This is useful if the user wants to
# cancel oauth signing in/up in the middle of the process,
# removing all OAuth session data.
# def cancel
# super
# end
# protected
# If you have extra params to permit, append them to the sanitizer.
# def configure_sign_up_params
# devise_parameter_sanitizer.permit(:sign_up, keys: [:attribute])
# end
# If you have extra params to permit, append them to the sanitizer.
# def configure_account_update_params
# devise_parameter_sanitizer.permit(:account_update, keys: [:attribute])
# end
# The path used after sign up.
# def after_sign_up_path_for(resource)
# super(resource)
# end
# The path used after sign up for inactive accounts.
# def after_inactive_sign_up_path_for(resource)
# super(resource)
# end
private
def soft_delete(user)
# 同じメールアドレスでも登録できるように、
# メールアドレスを“hoge@example.com_deleted_**********”に変更する
deleted_email = user.email + '_deleted_' + Time.current.to_i.to_s
user.assign_attributes(email: deleted_email, deleted_at: Time.current)
# 通常メールアドレスが変更されると通知メールが飛ぶので、
# その通知メールをキャンセルする
user.skip_email_changed_notification!
# 保存処理
user.save
end
end
ログインをオーバーライド
メールアドレスを論理削除で変更しているが、万が一書き換えたメールアドレスが判明された場合、ログインできてしまうことを考慮して認証をしっかりとしています。
# frozen_string_literal: true
class Users::SessionsController < Devise::SessionsController
# before_action :configure_sign_in_params, only: [:create]
# GET /resource/sign_in
# def new
# super
# end
# POST /resource/sign_in
def create
user = User.find_by(email: params[:user][:email])
if user.nil?
# メールアドレスに該当がない場合は、新規登録画面に遷移
# ユーザー登録されていない旨のフラッシュメッセージを仕込むならこのセクション
redirect_to new_user_registration_path
else
# パスワードの確認と、deleted_atにデータたが入っていないか
# deleted_atに日付が入っていれば再度ログイン画面へ遷移させる。
if user.valid_password?(params[:user][:password]) && user.deleted_at.nil?
# ログイン成功
# ログイン成功のフラッシュメッセージを仕込むならこのセクション
# ---
# ログイン情報をユーザー側に記録
sign_in user
# ログイン成功時の遷移
redirect_to root_path
else
# ログイン失敗時の遷移
# ---
# ログイン失敗のフラッシュメッセージを仕込むならこのセクション
redirect_to new_user_session_path
end
end
end
# DELETE /resource/sign_out
# def destroy
# super
# end
# protected
# If you have extra params to permit, append them to the sanitizer.
# def configure_sign_in_params
# devise_parameter_sanitizer.permit(:sign_in, keys: [:attribute])
# end
end
最後に
以上で、実装は完了のはずです。うまく動かない場合は、サンプルリポジトリを確認してください。