🌟

[Rails]退会機能

に公開

モデル

class User < ApplicationRecord
    devise :database_authenticatable, :registerable,
         :recoverable, :rememberable, :validatable
    # 退会処理・退会したユーザーはログインできない
    def active_for_authentication?
        super && is_active != false
    end
    
    def inactive_message
        is_active == false ? :inactive_account : super
    end
end

devise :database_authenticatable, :registerable,:recoverable, :rememberable, :validatable

これは Deviseに「どんな機能を使うか」伝える宣言

devise :database_authenticatable, :registerable,
       :recoverable, :rememberable, :validatable

これを書くことで、「このUserモデルにはDeviseのこれらの機能を使いたいです!」と指定している。

モジュール名 何をしてくれる?
:database_authenticatable パスワードによるログイン機能(DBで認証)
:registerable サインアップ機能(新規登録・編集・削除)
:recoverable パスワード忘れたときの再設定機能(メールで送信)
:rememberable 「ログイン状態を保持する」(remember me)
:validatable メール形式・パスワードのバリデーションを自動でやってくれる

active_for_authentication?

def active_for_authentication?
    super && is_active != false
end

active_for_authentication?

https://github.com/heartcombo/devise/wiki/How-To:-Customize-user-account-status-validation-when-logging-in
active_for_authentication? はDeviseに元からあるメソッドで、認証が有効かどうかを確認する。

super

super は、「親クラスの同じ名前のメソッドを呼び出す」というRubyの機能。

https://docs.ruby-lang.org/ja/latest/doc/spec=2fcall.html#super

この場合の親とは…?
active_for_authentication? メソッドは、元々は Devise::Models::Authenticatable というモジュールに定義されている。
https://github.com/heartcombo/devise/blob/main/lib/devise/models/authenticatable.rb
devise/lib/devise/models/authenticatable.rb

def active_for_authentication?
    true
end

inactive_message

Deviseがログイン失敗時に 「なぜログインできなかったのか?」 を伝えるために使うメソッド。

def inactive_message
  is_active == false ? :inactive_account : super
end

流れ

  1. ログインしようとする
  2. active_for_authentication? で「ログインできるか?」を判定
  3. もし false が返ってきたら…
  4. Deviseは inactive_message を呼び出して「どんな理由でログインできなかったのか?」を取得
  5. 画面にその理由に応じたエラーメッセージを表示する

config/locales/devise.ja.yml

inactive_account でエラーメッセージを出力する。

ja:
  devise:
    failure:
      inactive_account: "退会済みのアカウントです。再度ご登録ください。"

ユーザーをすでに登録済で途中から退会機能を追加した場合の処理

登録済ユーザーの退会状態を変更する(すべて true にする)

rails console
# ユーザーの `is_active` カラムの状態を確認する
User.pluck(:id, :is_active)

# 既存データの `is_active` カラムをすべて `true` にする
User.where(is_active: nil).update_all(is_active: true)

is_active カラムの初期値を true にする

すでにカラムがあって後から変更する場合

rails generate migration ChangeDefaultIsActiveOnUsers

できたマイグレーションファイルを以下に変更する。

class ChangeDefaultIsActiveOnUsers < ActiveRecord::Migration[6.1]
  def change
    change_column_default :users, :is_active, from: nil, to: true
  end
end
rails db:migrate

カラムを新規に追加する場合

rails generate migration AddIsActiveToUsers is_active:boolean

できたマイグレーションファイルを以下に変更する。

class AddIsActiveToUsers < ActiveRecord::Migration[6.1]
  def change
    add_column :users, :is_active, :boolean, default: true
  end
end
rails db:migrate

コントローラー

users_controller.rb

# 退会済のユーザーは一覧に表示しない
def index
    @users = User.where(is_active: true).page(params[:page])
end

# 退会アクション
def withdraw
    @user = User.find(params[:id])
    if @user == current_user
      # 退会状態に更新
      @user.update(is_active: false)
      # セッションをリセットしてログアウト
      reset_session
      redirect_to new_user_registration_path, notice: "退会処理が完了しました。ご利用ありがとうございました。"
    else
      redirect_to user_path(@user), alert: "不正な操作です。"
    end
end

@users = User.where(is_active: true).page(params[:page])

def index
  @users = User.where(is_active: true).page(params[:page])
end

User.where(is_active: true)

  • User モデルから 「退会していないユーザー」だけを抽出する。
  • is_activetrue のユーザーだけが対象。
    • is_active == false → 退会済み
    • is_active == true → アクティブ(表示対象)

https://railsguides.jp/active_record_querying.html

.page(params[:page])

  • これは ページネーション(ページ分割)のための処理。( kaminari を使用)
  • params[:page] には「今表示したいページ番号」が入る。

つまりこの行で

  • アクティブなユーザーだけを取得して
  • ページに分けて
  • @users に代入して
  • ビューで一覧表示できる状態にしている

tasks_controller.rb

# アクティブユーザーのタスクのみ表示
def index
    @tasks = Task.joins(:user).where(users: { is_active: true }).page(params[:page])
end

Task.joins(:user)

  • Task モデルが User に belongs_to :user している前提
  • joins(:user) は 「タスクとユーザーを結びつけて一緒に検索する」ためのSQL JOIN(内部結合)を意味する
    • タスクにひもづいたユーザーの情報も一緒に見ながら絞り込みたい
      SELECT * FROM tasks
      INNER JOIN users ON tasks.user_id = users.id
      WHERE users.is_active = true;
      

.where(users: { is_active: true })

  • 結合したユーザーの中から、is_active が true の人だけを対象にする。
  • つまり、「退会してないユーザーが作ったタスクだけ」を取得

ビュー

users/show.html.erb

<% if @user == current_user %>
    <%= link_to "退会", withdraw_user_path(@user), method: :patch, data: { confirm: "本当に退会しますか?" } %>
<% end %>

参考文献

https://qiita.com/ysk91_engineer/items/e19ae367dd5094217c03
https://qiita.com/Naggi-Goishi/items/49f758608f93286a3701

Discussion