【Rails】enumで対応ステータスを更新する


自作アプリを作成しているときに、管理者機能として、フォームからのお問い合わせの対応ステータスを更新する機能があったらいいなと思い、調べながら実装してみたので、メモしておきます!

実装画面はこんな感じです👇

前提

構成(モデル等は生成済の前提で進めます)

  • MODEL (FormInquiry)
    • t.integer "user_id"
    • t.text "content"
    • t.integer "response_status", default: 0
    • t.datetime "created_at"
    • t.datetime "updated_at"
  • CONTROLLER (form_inquiries)
    • 問い合わせ一覧(index action)
    • 問い合わせ詳細(show action)
    • 対応ステータス更新(update action)
  • VIEWS
    • 問い合わせ一覧(index.html)
    • 問い合わせ詳細(show.html)

ライブラリ
- enum_help(enumをI18n化するgem)
- devise(ユーザー/管理者認証)

その他
- 会員(エンドユーザー)をUser、管理者をAdminとしてそれぞれテーブルを作成しています。
- 会員からフォームで問い合わせを受け付けると、管理者の問い合わせ一覧画面に表示されます。
- 管理者は問い合わせ詳細画面を開き、対応ステータス(未対応/対応中/対応済)の更新を行えます。更新すると、フラッシュメッセージが表示され、問い合わせ一覧のステータスも更新されます。

環境
ruby 2.6.3
rails 5.2.6
OS:Linux(CentOS)
IDE:Cloud9

実装

enumとは?
「列挙型」という、整数が割り当てられた文字列を順に出力していく変数のことで、0を男性、1を女性のように、整数の値に何かしらの意味づけを行ってデータを管理することができます。「hashのkey部分の定数を保存すると、そのkeyに対応するvalueの整数が保存される仕組み」とのこと(【Rails】 enumチュートリアルより)

今回は、以下のような値で進めていくことにします。
{ 0: outstanding(未対応), 1: in_progress(対応中), 2: closed(対応済)}

STEP1. enumの定義

👇 FormInquiryモデルのresponse_statusカラムに対して、enumを定義します

app/models/form_inquiry.rb
belongs_to :user
enum response_status: { outstanding: 0, in_progress: 1, closed: 2 }

次に、enumを日本語化します。
👇 Gemfileにenum_helpを追記して、保存したらbundle installします。

Gemfile
gem 'enum_help'

👇 英語名に対応する日本語の値を、ja.ymlに定義します。

config/locales/ja.yml
ja:
  time:
    formats:
      default: "%Y/%m/%d %H:%M"
  enums:
    form_inquiry:
      response_status:
        outstanding: "未対応"
        in_progress: "対応中"
        closed: "対応済"

👇 もしアプリケーションのデフォルト言語が日本語になっていない場合は、設定しておきます

config/application.rb
module App
  class Application < Rails::Application
    # 略
    config.i18n.default_locale = :ja
  end
end

STEP2. コントローラーに記述

対応ステータスの更新はupdateアクションで行います。
更新できたら、問い合わせ詳細画面にリダイレクトし、フラッシュメッセージを表示するようにしています。

app/controllers/form_inquiry_controller.rb
class Admin::FormInquiriesController < ApplicationController
  before_action :authenticate_admin!
  before_action :set_form_inquiry, only: [:show, :update]

  def index
    @form_inquiries = FormInquiry.all.includes(:user).order(created_at: "DESC").page(params[:page]).per(10)
  end

  def show
    @user = User.find(@form_inquiry.user_id)
  end

  def update
    if @form_inquiry.update(form_inquiry_params)
      redirect_to admin_form_inquiry_path(@form_inquiry), notice: "対応ステータスを更新しました"
    else
      render :show, alert: "対応ステータスを更新できませんでした"
    end
  end

  private

  def form_inquiry_params
    params.require(:form_inquiry).permit(:response_status)
  end

  def set_form_inquiry
    @form_inquiry = FormInquiry.find(params[:id])
  end
end

STEP3. ビューの記述

問い合わせ詳細(show)

👇 対応ステータスの更新は、問い合わせ詳細画面で行うため、show.html.erbにセレクトボックスを実装します(※該当部分のみ抜粋しています)

app/views/form_inquiries/show.html.erb
   <%= form_with model: [:admin, @form_inquiry], method: :patch, local: true do |f| %>
     <%= f.select :response_status, FormInquiry.response_statuses.keys.map {|k| [I18n.t("enums.form_inquiry.response_status.#{k}"), k]} %>
     <%= f.submit "変更" %>
   <% end %>

ここでは、enumの値(未対応/対応中/対応済)のセレクトボックスを作成するため、mapメソッド(配列の要素の数だけブロック内で処理を繰り返して、新しい配列を返すメソッド)を使用します。

ここで何をしているか確認しておくと、

  • keysメソッドでハッシュオブジェクトからキーのみ配列の形で取り出している(outstanding/in_progress/closed
  • I18nのtranslateメソッド(I18n.t)でenumのキーの内容を翻訳している
  • keysメソッドで取り出した値一つ一つに対しmapメソッドで処理し、二次元配列(配列の中に、配列が格納された形の配列のこと)を戻り値として返す

試しにrails cで返り値を見てみると、こうなっています👇
[3]はtranslateメソッドを使わなかった場合を一応出力してみた形です。

二次元配列の2つ目の要素(["対応中", "in_progress"])を選択して、検証ツールで見てみると、この二次元配列の1つ目の要素がoptionタグの表示部分に、2つ目の要素がvalueに入る仕組みであることがわかります。

これが他2つの二次元配列に対しても展開されて、セレクトボックスが生成されているようですね。

問い合わせ一覧(index)

👇 7〜13行目で、一覧に表示する対応ステータスをif文を使って分岐させています。
日本語化した定数の値を表示したいので、カラム名の末尾に_i18nを追記します。

app/views/form_inquiries/index.html.erb
    <table>
      <% @form_inquiries.each do |form_inquiry| %>
        <tr>
          <td><%= l form_inquiry.created_at %></td>
          <td><%= link_to form_inquiry.user.name, admin_user_path(form_inquiry.user.id) %></td>
          <td>
            <% if form_inquiry.response_status == 'outstanding' %>
              <%= form_inquiry.response_status_i18n %>
            <% elsif form_inquiry.response_status == 'in_progress' %>
              <%= form_inquiry.response_status_i18n %>
            <% else %>
              <%= form_inquiry.response_status_i18n %>
            <% end %>
          </td>
          <td>
            <%= link_to "詳細", admin_form_inquiry_path(form_inquiry.id), class:'btn btn-sm py-0 btn-outline-secondary' %>
          </td>
        </tr>
      <% end %>
    </table>

これで対応ステータスが更新できるようになりました!

keysメソッドやmapメソッドの部分は、個人的にまだ理解が難しいのですが、言語化することで理解が少し深まったように思います。何かお気づきの点があれば、ぜひご教示ください。

参考資料