Rails6 Deviseを使ったユーザー退会処理方法

## はじめに
今回はユーザー退会処理の機能、退会後同じメールアドレスとパスワードではログインできないようにする方法を記載します。

## 開発環境、前提条件
* AWS Cloud9
* Ruby 3.1.2p20 (2022-04-12 revision 4491bb740a) [x86_64-linux]
* Rails 6.1.7.3
* Deviseのインストールが完了していて、ユーザーの新規登録やログインができる状態。


## Userテーブル定義
`is_deleted`カラムを作成し、デフォルトをfalseにしておきます。
退会ユーザーの`is_deleted`をtrueにすることで退会ユーザーのデータは残したまま、退会しているかどうかを区別できるようにします。

| カラム名      |       カラム説明 |       データ型 |    デフォルト    |
|:-----------------|------------------:|------------------:|:------------------:|
| ID             |              ユーザーID |              integer |                |
| name           |            ユーザー名 |          string |              |
| is_deleted            |              削除フラグ |              datetime |        FALASE        |
| created_at              |                登録日 |               datetime |         NOW         |
| updated_at           |             更新日 |             boolean |       NOW       |


## users.controllerの編集
`hide`アクションを作成します。
・`@user.update(is_deleted: true)`で、@userのis_deletedカラムをtrueにupdateします。
・`reset_session`で、ユーザーをログアウトさせます。
・`redirect_to root_path`で、ログアウト後ルートパスに飛ばします。
`index`アクションを作成します。
・`退会済みのユーザーは`、is_deletedカラムがtrueに設定されているため、このページに表示されません。

```app/controllers/users_controller.rb

   def index
     @users = User.where(is_deleted: false) #users/indexページでは、is_deletedカラムがfalseのユーザーのみが表示されます
   end

   def hide
    @user = User.find(params[:id])
    @user.update(is_deleted: true) #is_deletedカラムにフラグを立てる(defaultはfalse)
    reset_session
    flash[:notice] = "ありがとうございました。またのご利用を心よりお待ちしております。"
    redirect_to root_path
  end
```

しかしこのままでは直接URLに"user/:id"を入力するとユーザーの詳細情報を確認することができてしまいます。
そこで`users.controller`に追記しましょう。
```app/controllers/users_controller.rb
class UsersController < ApplicationController
  before_action :check_user_status,  only:[:show]
.
.
.

private

  def check_user_status
    user = User.find(params[:id])
    if user.is_deleted
      redirect_to root_path
  end
end
end
```
この記述ではshowアクションが呼び出される前に`check_user_status`メソッドが呼び出され、指定されたユーザーの`is_deleted`カラムがtrueかどうかチェックし、退会済みの場合は`root_path`にリダイレクトされます。
こうすることで直接URLを叩いても退会済みのユーザの詳細ページを閲覧することはできません。



## ルーティングの作成
`hide`アクションのルーティングを作成します。
`as: 'users_hide'`で、showアクションと間違われないようURL名を指定します。

```config/routes.rb
Rails.application.routes.draw do

  devise_for :users

    resources :users do
    member do
      delete :hide, as: 'users_hide'
    end
 end


end
```
## 退会ボタンの作成
### ユーザー編集ページ(users/:id/edit.html.erb)
`if user_signed_in? && @user.id == current_user.id`で、ユーザーがログインしていて且つログインユーザーであれば退会ボタンを表示するようにしています。
`"data-confirm" => "本当に退会しますか？"`で退会ボタンクリック時にアラートが出るようにしています。

```app/views/users/show.html

<div class="form_group">
    <% if user_signed_in? && @user.id == current_user.id %>
    <%= link_to "退会", users_hide_user_path(current_user), method: :delete, "data-confirm" => "本当に退会しますか？？", class: "btn btn-outline-danger" %>
    <% end %>
</div>
```

## 退会ユーザーはログインできなくする

### user.rb
ログインする時に退会済み(is_deleted==true)のユーザーを弾くためのメソッドを作成します。
`super && (self.is_deleted == false)`で、userのis_deletedがfalseならtrueを返すようにしています。
```app/models/user.rb
 #退会ユーザーはログインできなくする
  def actuve_for_authentication?
    super && (self.is_deleted == false)
  end
```

### sessions_controller.rb
・`if (@user.valid_password?(params[:user][:password])`で、入力されたパスワードが正しいことを確認しています。
・`(@user.active_for_authentication? == false))`で、@userのactive_for_authentication?メソッドがfalseであるかどうかを確認しています。
・上記の2点が当てはまれば、ログインページにリダイレクトし、エラーメッセージを表示するようにしています。

```app/controllers/sessions_controller.rb

class Users::SessionsController < Devise::SessionsController

  before_action :reject_user, only: [:create]

  protected

  def reject_user
    @user = User.find_by(email: params[:user][:email].downcase)
    if @user
      if (@user.valid_password?(params[:user][:password]) && (@user.active_for_authentication? == false))
        flash[:error] = "退会済みです。"
        redirect_to new_user_session_path
      end
    else
      flash[:error] = "必須項目を入力してください。"
    end
  end
end
```




## 便利なフラッシュメッセージの記述

通常フラッシュメッセージの表示は`<%= flash[:error] %>`とすると思いますが毎回ページごとに記述するよりも簡単な方法があります。

application.html.erbファイルに以下のコードを追加して、フラッシュメッセージが表示されるようにします。
```app/application.html.erb
<% flash.each do |key, value| %>
  <div class="alert alert-<%= key %>">
    <%= value %>
  </div>
<% end %>
```

このコードは、コントローラーで設定されたフラッシュメッセージをループで処理し、それぞれのメッセージを表示します。keyには:notice、:alertなどのフラッシュの種類が、valueにはメッセージの内容が入ります。

`BootStrap`をインストール済みの場合はフラッシュメッセージの配置を帰ることも可能です。以下例
```app/application.html.erb
<% flash.each do |key, value| %>
  <div class="alert alert-<%= key %> alert-dismissible fade show position-fixed fixed-top w-25 ml-auto mt-3 mr-3" role="alert">
    <%= value %>
    <button type="button" class="close" data-dismiss="alert" aria-label="Close">
      <span aria-hidden="true">&times;</span>
    </button>
  </div>
<% end %>
```
このコードでは、position-fixed, fixed-top, w-25, ml-auto, mt-3, mr-3 クラスを使用して、フラッシュメッセージを画面右上に配置しています。

以上で実装完了です。コメント、いいねお待ちしてます！！