こんにちは!TECH DRIVEのTedです。
今回はenumについて、enumをどのように使えば良いかについてのご紹介をしたいと思います。
はじめに
enumとは列挙型のことを指し1つの識別子が複数の定数をもつことができる型のことである・・・と、enumについて調べると色々知ることができます。 しかし、初めてenumに触れる人にとってはイメージが掴みにくいかもしれません。
今回も、Railsを題材に使ってenumをどのように使えば効果的に使えるのかをみていきたいと思います。 少なくとも現場でコードを書かせていただいている私も同様の使い方をするので1つの例になると思います。
それから今回は、よくenumと合わせて使われるgem enum_help
も合わせて使っていきます。
enumを宣言してみる
enumはよくレコードの状態を表現するために使われます。
例えば、WordPressのようなCMSにおける記事を模したPostというモデルがあった場合に、投稿には下書きというプロセスがあるはずです。
このような場合にenumが力を発揮してくれたりします。
そして今回は次にあげるような仕様で進めたいと思います。
UserとPostは1対多の関係にあり。PostモデルはUserの投稿になります。
他のユーザーに公開するかどうかの状態をpostsテーブルのstatus
というカラム持っていてこれをenumで管理して使い分ける場面です。
- 下書き
- 公開
- 非公開
まずは、postsテーブルにstatusカラムを加えます。
マイグレートファイル
class AddStatusToPost < ActiveRecord::Migration def change add_column :posts, :status, :integer, default: 0 end end
モデルはこんな感じ
class User < ApplicationRecord # nameとかemailとかカラムを持っている has_many :posts end class Post < ApplicationRecord # bodyとstatusのカラムを持っている belongs_to :user end
ここでPostのstatus
というカラムに対してenumを宣言します
class Post < ApplicationRecord # bodyとstatusのカラムを持っている belongs_to :user enum status: { draft: 0, published: 1, unpublished: 2 } # ここでenumを宣言する end
statusはinteger型で定義されたカラムなのでデータベース上に保存されるのは0か1か2のどれかになります。
上のマイグレートファイルで、statusカラムはdefault: 0
として定義されているので、以下のようになるはずです。
- draft:下書き(デフォルト)
- publish:公開済み
- unpublish:非公開
enumで使用するカラムはこのようにデフォルト値を設定しておくと扱いやすくなるでしょう。
statusをenumで宣言したので、この後プログラム上でstatusを扱う場合、それぞれの数字に割り当てられた状態(ここでいうdraft
,published
,unpublished
)として扱うことができます。
enumで使えるようになるメソッド
enumを宣言することで次のようなメソッドが使用できるようになります。
これが本当に使い勝手が良いのです。
post = Post.create(user_id: 1, status: :draft, body: "投稿の中身") # statusは status: 1 とかのintegerでも可能 # postの現在のstatusを出力 post.status # 出力 => draft # postのstatusがdraftかどうかをbooleanで返す post.draft? # 出力 => true # postのstatusがpublishedかどうかをbooleanで返す post.published? # 出力 => false # statusがunpublishedに更新される post.unpublished! # カラム名の複数形で状態のハッシュを取得 Post.statuses # 出力 => { draft: 0, published: 1, unpublished: 2 } # スコープとしても使える Post.published # publishedなpostレコードを検索する # Post.where(status: :published)
現場で使われるenumでは上のようなメソッドを使用しているケースが多いのでこの辺りを押さえておくことをオススメします。
メソッドを応用した様々なユースケース
ここまでで、enumを宣言すると起こることがつかめるようになると思います。
しかし、enumの恩恵はわかりましたが、「何をどうやって使えば良いか」というのがピンとこないと思います。
ここでenumを実際に使ったケースを3点ご紹介します。
セレクトボックス
入力フォームで状態をセレクトボックスで選ばせたいとかあると思います。
以下のような(題材とは項目が違いますがイメージです)
これはこんな風にやると実装できたりします。
<%= form_with(設定がある) do |form %> <%= form.select :post_config, Post.statuses%> <% end %>
先ほどご紹介したメソッドの中にstatases
というのがありましたね。
今回はそれを使っています。
状態ごとのフィルタリング
欲しい状態のレコードだけを検索したい、みたいな状況でenumが活躍します。
enumで提供されているスコープをそのまま活用するだけなのですが、実際現場でよく使われる方法なので「こう使われる」というところだけ抑えていると良いでしょう。
例えば公開済みの記事だけを取得したいとしましょう。
posts = Post.published
これでpostsには公開済みの投稿が格納されます。
では、あるユーザーの公開済みの投稿を取得の場合
user = User.find(1) user.posts.published
簡単に書くとこのようになります。
このようにenumのスコープは使い勝手が良い分、至る所で出会うことがあります。
更新処理
状態だけをサクッと変えたいとき、今回の場合だと下書きの記事をボタン1つで更新!としたい場合です。
statusカラムだけを更新したいけども、わざわざform_for
とかでフォームを作って送信!みたいにviewに書くとちょっと仰々しいです。
なので、viewではaタグ(link_to
で生成されるhtmlタグ)とかで更新用に作ったアクションにアクセスしてアクションの中でpublished!
を読んでしまうと楽に実装できたりします。
# view # PostsController#update_statusへのリンク <%= link_to "公開", [publishアクションへのパス], method: :post %>
class PostsController < ApplicationController def update_status post = Post.find(params[:id]) post.published! # ここでサクッと更新してしまう redirect_to xxx_path end end
日本語化(i18n)
最後に、enumの日本語化についてここで少し書いておこうと思います。
先ほどご紹介した、セレクトボックスをそのまま実装すると、enumで宣言したdraft
、published
、unpublished
が英語のまま表示されてしまいます。
またviewのページで post.status
みたいに書くと同様に英語で表示されることでしょう。
ここは次のように、i18n対応(日本語)にしておきたいところではあります。
- draft => 下書き
- publish => 公開
- unpublish => 非公開
ここでgem enum_help
の出番です。このgemを使うことでenumで列挙した項目のi18n(日本語)対応ができるようになります。
次のようなステップで日本語化の準備をしていきます。
app/config/locales
以下に日本語化ファイルに設定を書き込む
# app/config/locales/ja.yml ← ここは読み込めるのであれば名前はなんでも良い ja: enums: post: status: draft: 下書き published: 公開 unpublished: 非公開
- 実際に使うとき
enumを宣言したカラムを呼び出すときにカラム名の後に
_i18n
と書けばi18n対応ができます。
例えばPostモデルのstatusカラムの場合
post.status_i18n
となります。
さらに、先ほどのセレクトボックスも以下のように書き直すことができます。
form.select :status, Post.statuses_i18n.invert
と書くといい感じに日本語化されるでしょう。
Post.statuses_i18n => { "draft" => "下書き", "publish" => "公開", "unpublish" => "非公開" }
*invert
についてはrubyのhashオブジェクトのメソッドになるので今回ご紹介はしませんが調べておくと良いでしょう。
gem enum_help
はenumを使用するときにはほぼ必須のgemになりますので覚えておくと良いでしょう。
まとめ
enumを宣言するだけで便利なメソッドやスコープが簡単に使えるようになるのは、enumの素晴らしい利点だと思います。
今回のような状態を扱う実装を行う際は「enumは使えないだろうか?」と検討してみるのは良いことだと思います。
今回は投稿の下書き、公開についてでしたが、例えばユーザーが管理者であるか一般のユーザーであるかを分けるため、のような使い方もよく見かけます。
enumを使いこなすことでコードがスッキリして良いコードを書く手助けになることだと思いますので是非使ってみてください。
PR
TECH DRIVE協賛企業のサークルアラウンド株式会社では、プログラマーの成長を加速させるためのトレーニングを行なっています。フロントエンド/バックエンド問わず各種バリエーションがございますので、ご興味がある方は是非以下のリンクより詳細をご覧ください。
Ruby Climbing
週1からはじめられる「Ruby」でWEB開発の基礎が習得できる塾です。現役のプログラミング講師&Rubyエンジニアがプログラミング入門からフレームワーク(Sinatra/Ruby on Rails)を使用した本格的なWEB開発の学習までをしっかりとサポートします。
個別トレーニング
短期間でぐっと成長したい方は弊社主催の個別トレーニングがおすすめです。 トレーニング内容は、受講者の方の課題/要望をお伺いした上で、フルオーダメイドで作成させていただきます。 詳細は以下のリンクよりご確認ください。(応募者多数の場合には時間を別途ご用意する予定です)。