こんにちは!oda@エンジニア1年目です!
業務では、今まで使っていなかったメソッドなど、様々なコードに触れる機会があります。
今回は、その中でもRuby on Railsのenumについて、整理してみたいと思います。
(以下、Rails 7.0.2.3を使用しています。)
はじめに
enumとは、属性で使う値を名前で参照できるようにする仕組みのことです。
これだけでは、非常にわかりにくいので、以下のコードを実行しながら、確認してみます。
事前準備
まずはFoodsテーブルを作成します。
bin/rails g model Foods name:string category:integer
models/food.rbを以下のように編集して、categoryにenumを設定します。
この時、以下のようにenumを配列で定義すると、:meatには0が、:vegetableには1が、:fruitには2が定義されます。
class Food < ApplicationRecord enum :category, [ :meat, :vegetable, :fruit ] end
次に、サンプルデータを作成します。
Food.create(name: "牛肉", category: :meat) Food.create(name: "鶏肉", category: :meat) Food.create(name: "トマト", category: :vegetable) Food.create(name: "にんじん", category: :vegetable) Food.create(name: "玉ねぎ", category: :vegetable) Food.create(name: "りんご", category: :fruit) Food.create(name: "みかん", category: :fruit) Food.create(name: "バナナ", category: :fruit) Food.create(name: "ぶどう", category: :fruit) Food.create(name: "鯖")
enumの動作を確認
まずは、DBを確認してみます。categoryには、数値でデータが保存されています。
id | name | category ----+----------+---------- 1 | 牛肉 | 0 2 | 鶏肉 | 0 3 | トマト | 1 4 | にんじん | 1 5 | 玉ねぎ | 1 6 | りんご | 2 7 | みかん | 2 8 | バナナ | 2 9 | ぶどう | 2 10 | 鯖 |
以下のように、categoryを呼び出してみると、定義した名前が返ってきます。
なお、鯖のようにcategoryが登録されていない場合は、nil
が返ってきます。
food1 = Food.find(1) food1.name # => "牛肉" food1.category # => "meat" food10 = Food.find(10) food10.name # => "鯖" food10.category # => nil
また、enumで定義した名前には、自動的にscopeが定義されるため、以下のように呼び出すことが可能です。
scopeとは、モデルに対して実行したいクエリを設定する仕組みです。
なお、notを付けて呼び出した場合、categoryがnilのものは呼び出されないので、注意が必要です。
foods_vegetable = Food.vegetable foods_vegetable.all # => [#<Food:0x00007f8803e4b758 id: 3, name: "トマト", category: "vegetable">, #<Food:0x00007f8803e4b690 id: 4, name: "にんじん", category: "vegetable">, #<Food:0x00007f8803e4b5c8 id: 5, name: "玉ねぎ", category: "vegetable">] foods_not_vegetable = Food.not_vegetable foods_not_vegetable.all # => [#<Food:0x00007f8803e08ca0 id: 1, name: "牛肉", category: "meat">, #<Food:0x00007f8803e08bd8 id: 2, name: "鶏肉", category: "meat">, #<Food:0x00007f8803e08b10 id: 6, name: "りんご", category: "fruit">, #<Food:0x00007f8803e08a48 id: 7, name: "みかん", category: "fruit">, #<Food:0x00007f8803e08980 id: 8, name: "バナナ", category: "fruit">, #<Food:0x00007f8803e088b8 id: 9, name: "ぶどう", category: "fruit">]
さて、それではenumで設定されていないデータを登録しようとするとどうなるのでしょうか。
以下のとおり、エラーが発生し、登録はできません。
Food.create(name: '枝豆', category: :bean) # => `assert_valid_value': 'bean' is not a valid category (ArgumentError)
DBはそのままで、次のようにenumを追加するとどのようになるでしょうか。
class Food < ApplicationRecord enum :category, [ :meat, :fish, # ←ここに追加 :vegetable, :fruit ] end
試しに、トマトを見てみます。
tomato = Food.find(3) # => #<Food:0x00007fd188546ad0 id: 3, name: "トマト", category: "fish">
トマトのcategoryがfishに変わってしまいました。
このように、配列の途中に追加すると、enumの採番が変わってしまうので、追加する場合は、最後にする必要があります。
なお、上記のenumは次のようにハッシュを使って、明示的に定義することもできます。
class Food < ApplicationRecord enum :category, { meat: 0, vegetable: 1, fruit: 2 } end
ハッシュと使うと、次のように連番でない値を設定することもできます。
class Food < ApplicationRecord enum :category, { meat: 0, vegetable: 1, fruit: 2, others: 9 } end
今回は以上となります。
ご覧いただき、ありがとうございました!