[Rails]enum_helpとransackを使ったプルダウン検索 20/20
はじめに
管理者画面のユーザ一覧画面で、ransack
とenum_help
を使ってプルダウン検索機能を実装していきます。
ransack
をすでにインストール済みなので、enum_help
のインストールから行なっていきます。
環境:
Rails 6.1.7.3
ruby 3.0.0
enum_help
をインストールする
gem 'enum_help'
bundle install
訳文を追加する
ja:
activerrecord:
enums:
user:
role:
general: '一般'
admin: '管理者'
コンソールでi18n
メソッドを使って確認します。
irb(main):002:0> User.roles
=> {"general"=>0, "admin"=>1}
irb(main):003:0> User.roles_i18n
=> {"general"=>"一般", "admin"=>"管理者"}
検索オブジェクトを作成する
class Admin::UsersController < Admin::BaseController
skip_before_action :check_admin
before_action :set_user, only: %i[show edit update destroy]
def index
@search = User.ransack(params[:q])
@users = @search.result(distinct: true).order(created_at: :desc).page(params[:page])
end
検索フォームを作成する
<%= search_form_for @search, url: admin_users_path do |f| %>
<div class="row">
<div class="form-inline align-items-center mx-auto">
<div class="col-auto">
<%= f.search_field :first_name_or_last_name_cont, class: 'form-control', placeholder: t('defaults.search_word') %>
</div>
<div class="col-auto">
<%= f.select :role_eq, User.roles_i18n.invert.map{|key, value| [key, User.roles[value]]}, { include_blank: t('defaults.unspecified') }, { class: 'form-control mr-1' } %>
</div>
<div class="col-auto">
<%= f.submit class: 'btn btn-primary' %>
</div>
</div>
</div>
<% end %>
search_form_for
メソッドは、検索用のフォームを作成するためのヘルパーメソッドです。
@search
オブジェクトを使用して検索フォームを作成しています。@search
は、検索のためのパラメーターを格納するために使用されるオブジェクトです。
url: admin_users_path
は、フォームが送信された際のアクション先のURLを指定しています。
<%= f.select :role_eq, ...%>
は、ロールの絞り込み用のプルダウンを表示しています。:role_eq
は絞り込みのためのカラム名を指定しています。
eq
はransack
のメッチャとなります。
role_eq
はrole
カラムに等しいものを探すとのことです。
invert
はハッシューのキーと値を入れ替えうメソッドです。
f.select
f.select
は、Railsのフォームヘルパーメソッドの一つで、ドロップダウンリスト(セレクトボックス)を生成するために使用されます。
例えば、User
モデルにrole
という列挙型(enum)があるとします。このrole
列挙型には、admin
とgeneral
の2つの値があります。その場合、f.select
を使用してrole
を選択するドロップダウンリストを作成することができます。
<%= f.select :role, User.roles.keys, prompt: 'ロールを選択してください', class: 'form-control' %>
User.roles.keys
は、User
モデルのrole
列挙型の全ての値(admin
とgeneral
)を配列として取得します。
irb(main):003:0> User.roles.keys
=> ["general", "admin"]
prompt
オプションは、ドロップダウンリストの最初の選択肢として表示されるテキストを指定します。class
オプションは、ドロップダウンリストのスタイルを指定するためのCSSクラスを指定します。
他のオプションについて公式ガイドを参考にしてみてください。
User.roles_i18n.invert.map{|key, value| [key, User.roles[value]]}
ransackはenumで定義した文字列に対応しておらず、保存される値のvalueを0や1などのinteger形式で渡す必要があります。
irb(main):004:0> User.roles_i18n
=> {"general"=>"一般", "admin"=>"管理者"}
irb(main):005:0> User.roles_i18n.invert
=> {"一般"=>"general", "管理者"=>"admin"}
irb(main):006:0> User.roles_i18n.invert.map { |value, key| [value, key] }
=> [["一般", "general"], ["管理者", "admin"]]
irb(main):007:0> User.roles["general"]
=> 0
irb(main):008:0> User.roles["admin"]
=> 1
irb(main):009:0> User.roles_i18n.invert.map {|key,value| [key,User.roles[value]]}
=> [["一般", 0], ["管理者", 1]]
HTMLでは値が0
と1
になってますね。
訳文を追加する
ja:
unspecified: '指定なし'
コンソールで動作を確認します。
Started PATCH "/admin/users/2" for ::1 at 2023-07-10 12:59:28 +0900
Processing by Admin::UsersController#update as HTML
Parameters: {"authenticity_token"=>"[FILTERED]", "user"=>{"email"=>"user@test.com", "first_name"=>"", "profile_cache"=>"", "role"=>"admin"}, "commit"=>"更新する", "id"=>"2"}
User Load (0.1ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT ? [["id", 1], ["LIMIT", 1]]
↳ app/controllers/application_controller.rb:10:in `set_current_user'
User Load (0.1ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT ? [["id", 2], ["LIMIT", 1]]
TRANSACTION (0.1ms) begin transaction
↳ app/controllers/admin/users_controller.rb:15:in `update'
User Exists? (0.2ms) SELECT 1 AS one FROM "users" WHERE "users"."email" = ? AND "users"."id" != ? LIMIT ? [["email", "user@test.com"], ["id", 2], ["LIMIT", 1]]
↳ app/controllers/admin/users_controller.rb:15:in `update'
# integerに保存される
User Update (2.1ms) UPDATE "users" SET "updated_at" = ?, "role" = ? WHERE "users"."id" = ? [["updated_at", "2023-07-10 03:59:28.534792"], ["role", 1], ["id", 2]]
↳ app/controllers/admin/users_controller.rb:15:in `update'
TRANSACTION (0.8ms) commit transaction
↳ app/controllers/admin/users_controller.rb:15:in `update'
Redirected to http://localhost:3000/admin/users/2
Completed 302 Found in 16ms (ActiveRecord: 3.4ms | Allocations: 6118)
終わりに
プルダウンの実装がよくあるので慣れていきましょう。
Discussion