Rails

【Rails】状態管理を容易に実装することができる機能「enum」の使い方まとめ

Rails

 

今回は状態管理を容易に実装することができる機能「enum」の使い方についてまとめました。enumは手軽に扱うことができるだけでなく、様々な便利メソッドが使えるため、Railsの状態管理でよく使われる機能の1つになります。

enumとは

enum(= イーナム)とはRails4.1から追加されたモジュールで、1つのカラムに複数の定数を保存することができる機能です。また文字に整数を割り当てることもできます。

# 例(draftを1、publishを2、quitを3として割り当てる)
class Post < ApplicationRecord
  enum status: { draft: 1, publish: 2, quit: 3 }
end
enumは直訳で「列挙型」という意味になります。

定義方法

enumはハッシュや配列(要素は文字列またはシンボル)の形でモデルに定義します。

以下は投稿を管理するPostモデルにdraft/publish/quitの3つのステータスをハッシュと配列(文字列とシンボル)、それぞれの定義方法で記述した例になります。

# ハッシュでenumを定義した場合
class Post < ApplicationRecord
  enum status: { draft: 1, publish: 2, quit: 3 }
end

# 配列(文字列)でenumを定義した場合
class Post < ApplicationRecord
  enum status: [ "draft", "publish", "quit" ]
end

# 配列(シンボル)でenumを定義した場合
class Post < ApplicationRecord
  enum status: [ :draft, :publish, :quit ]
end

ハッシュと配列で定義した場合の違い

ハッシュの場合

ハッシュの場合は定義した数字がDBに保存されます。

# ハッシュでenumを定義した場合
class Post < ApplicationRecord
  enum status: { draft: 1, publish: 2, quit: 3 }
end
% Post.create(status: 'draft')
=> statusカラムには1が保存される

% Post.create(status: 'quit')
=> statusカラムには3が保存される

配列の場合

配列の場合は0から始まるインデックス番号がDBに保存されます。

class Post < ApplicationRecord
  enum status: [ "draft", "publish", "quit" ]
end
% Post.create(status: 'draft')
=> statusカラムには0が保存される

% Post.create(status: 'quit')
=> statusカラムには2が保存される

enumのメリット

enumを使用する主なメリットとしては以下の3点が挙げられます。

  • 改修が容易
  • 便利なメソッドが使用できる
  • 不正な値がDBに保存されることを防ぐことができる

改修が容易

enumの1つ目のメリットとしては改修の容易さが挙げられます。

先程のステータスを管理するenumで見ていきます。

class Post < ApplicationRecord
 enum status: { draft: 1, publish: 2, quit: 3 }
end

 

ここに新たなステータス「レビュー待ち(review)」というステータス追加が必要になった場合は、statusにreview: 4の記述を追加するだけです。

class Post < ApplicationRecord
  enum status: { draft: 1, publish: 2, quit: 3, review: 4 }
end

 

このようにenumで状態を管理することで、状態追加や削除を簡単に行うことができるというメリットがあります。

便利なメソッドが使用できる

2つ目のメリットは便利なメソッドが使用できることです。

enumには以下のような便利なメソッドが用意されているため、実装コストを減らすことができるというメリットもあります。

class Post < ApplicationRecord
 enum status: { draft: 1, publish: 2, quit: 3 }
end
# draftステータスのレコードを作成
% Post.create(status: "draft")

# draftステータスか確認
% Post.last.draft?
=> true

# publishステータスか確認
% Post.last.publish?
=> false

# quitステータスか確認
% Post.last.quit?
=> false
# draftステータスのレコードを作成
% Post.create(status: "draft")

# publishステータスに更新
% Post.last.publish!

# draftステータスか確認
% Post.last.draft?
=> true

# publishステータスか確認
% Post.last.publish?
=> true

# quitステータスか確認
% Post.last.quit?
=> false

不正な値がDBに保存されることを防ぐことができる

3つ目のメリットは不正な値の保存を防ぐことができることです。

enumに定義した値以外を保存しようとするとエラーになるため、予期せぬ値の保存を防ぐことができます。

class Post < ApplicationRecord
 enum status: { draft: 1, publish: 2, quit: 3 }
end
% Post.create(status: "review")
=> 保存されない

enumを使ったミニアプリの作成

実際にenumを使ったミニアプリを作成しながら、enumの使い方について見ていきます。

今回はユーザーの権限を管理するミニアプリを想定します。

新規アプリケーションの作成

まずは以下のコマンドで新規アプリケーションを作成します。

% rails new enum_sample -d mysql
% cd enum_sample
% rails db:create
  • アプリケーション名: enum_sample
  • Ruby: 3.1.2
  • Rails: 7.0.4.1
  • データベース: MySQL

Userの雛形を作成

次にScaffoldの機能を用いて、string型のnameカラム(名前)とinteger型のgenderカラム(性別)、integer型のroleカラム(権限)を持つUserモデルを作成します。

今回はgender(性別)とrole(権限)をenumで管理します。

% rails g scaffold User name:string gender:integer role:integer
% rails db:migrate
enumのカラムには数字が保存されるため、integer型(またはboolean型)で作成する必要があります。
Rails
【Rails】秒速でアプリケーション開発できるscaffoldの使い方を簡単にまとめてみた手っ取り早くアプリケーションを開発することができるscaffold(スキャフォールド)の使い方について簡単に解説しています。新しい機能実装をテストしてみたい、すぐにでもアプリケーションを立ち上げたいといった場合に便利な機能なので、ぜひ覚えておきましょう。...

enumの定義

先程作成したUserモデルにenumを定義します。

権限の強さはmaster(マスター権限)>admin(管理者権限)>general(一般権限)としています。

# app/models/user.rb

class User < ApplicationRecord
  enum gender: { man: 0, woman: 1, other: 2 }
  enum role: { master: 0, admin: 1, general: 2 }
end

ビューの編集

以下のようにビューファイルを編集します。

# app/views/users/index.html.erb

<p style="color: green"><%= notice %></p>
<h1>ユーザー一覧</h1>
<div id="users">
  <% @users.each do |user| %>
    <%= render user %>
    <p>
      <%= link_to "詳細", user %>
    </p>
  <% end %>
</div>
<%= link_to "新規作成", new_user_path %>
# app/views/users/new.html.erb

<h1>新規ユーザー作成</h1>
<%= render "form", user: @user %>
<br>
<div>
  <%= link_to "一覧に戻る", users_path %>
</div>
# app/views/users/edit.html.erb

<h1>編集</h1>
<%= render "form", user: @user %>
<br>
<div>
  <%= link_to "詳細", @user %> |
  <%= link_to "一覧に戻る", users_path %>
</div>
# app/views/users/show.html.erb

<p style="color: green"><%= notice %></p>
<%= render @user %>
<div>
  <%= link_to "編集", edit_user_path(@user) %> |
  <%= link_to "一覧に戻る", users_path %>
  <%= button_to "削除", @user, method: :delete %>
</div>
# app/views/users/_form.html.erb

<%= form_with(model: user) do |form| %>
  <% if user.errors.any? %>
    <div style="color: red">
      <h2><%= pluralize(user.errors.count, "error") %> prohibited this user from being saved:</h2>

      <ul>
        <% user.errors.each do |error| %>
          <li><%= error.full_message %></li>
        <% end %>
      </ul>
    </div>
  <% end %>

  <div>
    <%= form.label :name, style: "display: block" %>
    <%= form.text_field :name %>
  </div>

  <div>
    <p>性別</p>
    <%= form.label :man %>
    <%= form.radio_button :gender, :man %>
    <%= form.label :woman %>
    <%= form.radio_button :gender, :woman %>
    <%= form.label :other %>
    <%= form.radio_button :gender, :other %>
  </div>

  <div>
    <p>権限</p>
    <%= form.label :master %>
    <%= form.radio_button :role, :master %>
    <%= form.label :admin %>
    <%= form.radio_button :role, :admin %>
    <%= form.label :general %>
    <%= form.radio_button :role, :general %>
  </div>

  <br>
  <div>
    <%= form.submit %>
  </div>
<% end %>
# app/views/users/_user.html.erb

<div id="<%= dom_id user %>">
  <p>
    <strong>名前:</strong>
    <%= user.name %>
  </p>
  <p>
    <strong>性別:</strong>
    <%= user.gender %>
  </p>
  <p>
    <strong>権限:</strong>
    <%= user.role %>
  </p>
</div>

コントローラの編集

コントローラを以下のように編集します。

# app/controllers/users_controller.rb

class UsersController < ApplicationController
  before_action :set_user, only: %i[ show edit update destroy ]

  def index
    @users = User.all
  end

  def show
  end

  def new
    @user = User.new
  end

  def edit
  end

  def create
    @user = User.new(user_params)

    respond_to do |format|
      if @user.save
        format.html { redirect_to user_url(@user), notice: "ユーザーが作成されました" }
        format.json { render :show, status: :created, location: @user }
      else
        format.html { render :new, status: :unprocessable_entity }
        format.json { render json: @user.errors, status: :unprocessable_entity }
      end
    end
  end

  def update
    respond_to do |format|
      if @user.update(user_params)
        format.html { redirect_to user_url(@user), notice: "ユーザーが更新されました" }
        format.json { render :show, status: :ok, location: @user }
      else
        format.html { render :edit, status: :unprocessable_entity }
        format.json { render json: @user.errors, status: :unprocessable_entity }
      end
    end
  end

  def destroy
    @user.destroy

    respond_to do |format|
      format.html { redirect_to users_url, notice: "ユーザーが削除されました" }
      format.json { head :no_content }
    end
  end

  private
    def set_user
      @user = User.find(params[:id])
    end

    def user_params
      params.require(:user).permit(:name, :gender, :role)
    end
end

翻訳設定

翻訳設定を追加します。

module EnumSample
  class Application < Rails::Application
    config.load_defaults 7.0

    # 以下の記述を追記
    config.i18n.default_locale = :ja
  end
end
% touch config/locales/ja.yml
# config/locales/ja.yml

ja:
  activerecord:
    models:
      user: ユーザー
    attributes:
      user:
        name: 名前
  helpers:
    submit:
      create: 登録
      update: 更新

挙動確認

ユーザー登録
Image from Gyazo
ユーザー編集
Image from Gyazo
ユーザー削除
Image from Gyazo

enumの翻訳設定

ここまでで一通りの設定はできましたが、よく見るとenum部分のみ英語表記のまま表示されてしまっています。

enum翻訳前

 

次はこの部分の翻訳を行います。

enumの翻訳には「enum_help」というGemが用意されているのでそれを使用します。

# Gemfile

gem 'enum_help'
% bundle install

 

ja.ymlを以下のように編集します。

# config/locales/ja.yml

ja:
  activerecord:
    models:
      user: ユーザー
    attributes:
      user:
        name: 名前
  enums:
    user:
      gender:
        man: 男性
        woman: 女性
        other: その他
      role:
        master: マスター
        admin: 管理者
        general: 一般
  helpers:
    submit:
      create: 登録
      update: 更新

 

app/views/users/_user.html.erbを以下のように編集します。

<div id="<%= dom_id user %>">
  <p>
    <strong>名前:</strong>
    <%= user.name %>
  </p>
  <p>
    <strong>性別:</strong>
    <%= user.gender_i18n %>
  </p>

  <p>
    <strong>権限:</strong>
    <%= user.role_i18n %>
  </p>
</div>
role_i18nのように{enumのカラム名}_i18nとすることで翻訳結果を表示させることができます。

 

ここまででenumの翻訳が反映されるはずです。

enum翻訳後

まとめ

  • enum(= イーナム)とはRails4.1から追加されたモジュールで、1つのカラムに複数の定数を保存することができる機能
  • enumはハッシュや配列(要素は文字列またはシンボル)の形でモデルに定義する
  • ハッシュで定義した場合は指定した数字がDBに保存される
  • 配列で定義した場合は0から始まるインデックス番号がDBに保存される
  • enumのメリットとしては「改修が容易」「便利なメソッドが使用できる」「不正な値がDBに保存されることを防ぐことができる」が挙げられる

参考

 

今回は状態管理を容易に実装することができる機能「enum」の使い方についてまとめました。状態管理といったenumが使える所は積極的に使い、読みやすいコードになるよう心がけていきましょう。