【Rails】 退会機能を論理削除で実装する

はじめに
---
gemのdeviseを使用したユーザー機能に論理削除を実装したので、自身の備忘録として、また今後実装を考えておられる方の参考になればと思い投稿させていただいております。

（参考サイト）
・[Rails]paranoiaを使わずに退会機能を実装する(ユーザーを論理削除する)
https://qiita.com/yuto_1014/items/358d0a425193b12c969a
<br>

開発環境
---
ruby 2.6.3
Rails 5.2.4.4
<br>

論理削除と物理削除について
---
今回は、ユーザー機能に論理削除を実装しましたので、こちらを例にご説明致します。
####●論理削除とは...
退会処理が行われるとユーザーはアカウントを使用できなくなります。
ただし、データ自体は記憶媒体（データベース）には存在します。

#####メリット
ー 誤って削除をしてしまった場合でも、データを復旧することができる。
ー 削除したデータも検索等で参照できる。

#####デメリット
ー データ量が膨大になり、データベースの容量の圧迫する可能性がある。
　（結果として、コストパフォーマンスを損なう可能性にも繋がる。）

---
####●物理削除とは...
退会処理が行われるとユーザーはアカウントを使用できなくなります。
そして、データ自体も記憶媒体（データベース）から削除されます。


#####メリット
ー 論理削除と比較すると、実装が用意（destroyアクションの活用）。
ー 削除をすると、データ自体も削除されるのでデータベースの容量を圧迫しない。

#####デメリット
ー 一度削除してしまうと、データの復旧が出来ない。
<br>

Gemを導入
---
```Gemfile.
gem 'devise'
```
```terminal.
$ bundle install
$ rails g devise:install
$ rails g devise user name:string　is_deleted:boolean
$ rails g devise:views
```

deviseを用いてユーザー機能を実装する
---
※ is_deletedがデフォルトでfalseの場合は、有効な状態です。
　反対にis_deletedがtrueにupdateされた時は退会している状態になります。

|モデル名 |カラム名   |データ型  |デフォルト|
|:--    |:--       |:--     |:--    |
|User　　|id        |integer|        |
|    　　|name      |string |        |
|    　　|email     |string |        |
|    　　|caption   |string |        |
|    　　|is_deleted|boolean|false   |
<br>

ルーティング
---
論理削除用に任意の名前のルーティングを作成する。

```routes.rb
Rails.application.routes.draw do
  root 'homes#top'
  devise_for :users
  resources :users, only: [:show, :edit, :update]
  # 退会確認画面
  get '/users/:id/unsubscribe' => 'users#unsubscribe', as: 'unsubscribe'
  # 論理削除用のルーティング
  patch '/users/:id/withdrawal' => 'users#withdrawal', as: 'withdrawal'
end
```
<br>

退会画面の作成
---
退会画面を表示させ、ユーザーに確認をしてもらいます。
更に、data: { confilm: "本当に退会しますか？" } こちらの部分のコードにより、退会するボタンが押された時に、「本当に退会しますか？」の文言が画面に表示されるようになっています。
退会画面と退会ボタンを押した後の確認画面の２度の操作を挟むことにより、手違いによる退会を防いでおります。

```users/unsubscribe.html.erb
<h2>退会お手続き</h2>
 <p>
   退会手続きをされますと、全てのサービスがご利用いただけなくなります。<br>
   再度ご利用をいただく場合には、新規登録のお手続きが必要になります。<br>
   本当に退会してよろしいですか？
 </p>
  <%= link_to "退会する", withdrawal_path, method: :patch, data: { confilm: "本当に退会しますか？" } %>
  <%= link_to "退会しない", user_path(current_user) %>
```
<br>

コントローラー
---
(説明)

● @user.update(is_deleted: true)
　　パラメータにより受け取ったidの＠userが持つ、is_deletedカラムをtrueにupdateする事により、退会状態を作り出します。

● reset_session
　　セッション情報を全て削除します。

● redirect_to root_path
　　退会後は、トップ画面に遷移します。

※Sessionとは
・ページ遷移しても以前入力した情報を保持することができる機能のこと。
・sessionが残ると、セキュリティ面のリスクがある。
・sessionは明示的に削除されるまで消えない。

```users_controller.rb
class UsersController < ApplicationController

  def withdrawal
    @user = User.find(params[:id])
    # is_deletedカラムをtrueに変更することにより削除フラグを立てる
    @user.update(is_deleted: true)
    reset_session
    flash[:notice] = "退会処理を実行いたしました"
    redirect_to root_path
  end

end
```
<br>

モデルに制約をかける
---
ログイン時に退会済みのユーザーが同じアカウントでログイン出来ないよう制約を設けています。

```models/user.rb
class User < ApplicationRecord
  # is_deletedがfalseならtrueを返すようにしている
  def active_for_authentication?
    super && (is_deleted == false)
  end
end
```

sessions_controller.rb
---
退会処理を行った会員が、同じアカウントでログイン出来ないようにします。

（説明）
● ＠user = User.find_by(name: params[:user][:name])
　　ログイン時に入力された名前に対応するユーザーが存在するか探しています。

● @user.valid_password?(params[:user][:password])
　　入力されたパスワードが正しいことを確認しています。

● @user.is_deleted == true)
　　＠userのactive_for_authentication?メソッドがfalseであるかどうか。




```users/sessions_controller.rb
class Users::SessionsController < Devise::SessionsController

  protected

  # 会員の論理削除のための記述。退会後は、同じアカウントでは利用できない。
  def reject_user
    @user = User.find_by(name: params[:user][:name])
    if @user 
      if @user.valid_password?(params[:user][:password]) && (@user.is_deleted == false)
        flash[:notice] = "退会済みです。再度ご登録をしてご利用ください。"
        redirect_to new_user_registration
      else
        flash[:notice] = "項目を入力してください"
      end
    end
  end
end
```

終わり
---
今回は以上になります。
私自身もプログラミング初心者ですが、同じ様な立場の方に少しでも参考になれば幸いです。
また、もし内容に誤りなどがございましたら、ご指摘いただけますと幸いです。