はじめに
管理者画面のユーザ一覧画面で、gem ransack
とenum_help
を使ってプルダウン検索機能を実装していく方法を見ていきます。
なお、今回はransack
とi18n
は既に導入済みとします。詳しいインストール方法は別の記事で紹介しているため、割愛させていただきます。

作業の流れ
- enum_helpの導入
- コントローラ
- 検索フォームの作成
enum_helpの導入
enum_help
は、enumで定義された文字列をi18nに対応させることができるgemです。
enum_help
を使って日本語化させていきましょう。
gem 'enum_help'
$ bundle install
ロケールファイルに追記
locales/activerecords/ja.yml
ファイルでenumの設定を記載します。
ja:
activerrecord:
enums:
user:
role:
general: '一般'
admin: '管理者'
これでenum_help
によるメソッドが使えるようになりました。
使い方
コンソールでメソッドを使ってみて、使い方を確認してみましょう。
irb(main):001:0> user = User.first
User Load (0.5ms) SELECT "users".* FROM "users" ORDER BY "users"."id" ASC LIMIT ? [["LIMIT", 1]]
=> #<User id: 1, email: "hogehoge@example.com", crypted_password: "$2a$10$1xKQipSf/sQ66t/R9L7kEePHY.wUdYgOWw/IeZ.xU2n...", salt: "cSLVZbzTssC2x3rx8ZiQ", last_name: "hoge", first_name: "hoge", created_at: "2021-08-28 22:21:26", updated_at: "2021-10-15 14:53:00", avatar: "f_f_object_115_s512_f_object_115_0bg.png", reset_password_token: nil, reset_password_token_expires_at: nil, reset_password_email_sent_at: "2021-10-11 03:33:04", access_count_to_reset_password_page: 0, role: "admin">
irb(main):002:0> user.role
=> "admin"
irb(main):003:0> user.role_i18n
=> "管理者"
irb(main):004:0> User.roles
=> {"general"=>0, "admin"=>1}
irb(main):005:0> User.roles_i18n
=> {"general"=>"一般", "admin"=>"管理者"}
userに対してrole_i18n
インスタンスメソッドを使うことができます。
Userクラスに対して、roles_i18n
というクラスメソッドを使うことで、{ キー1 ⇒ 値1, キー2 ⇒ 値2,... }
という形式でハッシュを取得できます。値がロケールファイルで定義した日本語に対応しています。
コントローラ
class Admin::UsersController < Admin::BaseController
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| %> # admin/users#indexへリクエストを投げる
<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 %>
コードを紐解く
<%= 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' } %>
f.select
についてよく分かっていない方は、まずこちらの記事を参考にしましょう。こちらはf.select
の基本の形です。
<%= f.select :保存されるカラム名, [ ["表示される文字","保存される値"], ["表示される文字","保存される値"], {オプション}, {htmlオプション} ] %>
[第一引数] role_eq
eq
は、”equals”の省略で、ransackで用意されている検索用の特殊な述語(Predicate)の一種です。
参考:https://nekorails.hatenablog.com/entry/2017/05/31/173925#006-eq—検索
role_eq
はと言うと、role
カラムに等しいものを探す。ということです。
[第二引数] User.roles_i18n.invert.map{|key, value| [key, User.roles[value]]}
enum_help
を導入したことにより、roles_i18n
というクラスメソッドが使えるようになりました。User.roles_i18n
によって{キー1 => 値1, キー2 => 値… }
というハッシュを取得しています。
irb(main):003:0> User.roles_i18n
=> {"general"=>"一般", "admin"=>"管理者"}
f.select
の基本の形を見てみると、["表示される文字","保存される値"]
となっています。今のままだと逆なので、入れ替えるメソッドを使います。
invert
メソッドは、値からキーへのハッシュを作成して返します。
irb(main):009:0> User.roles_i18n.invert
=> {"一般"=>"general", "管理者"=>"admin"}
map
メソッドを使ってハッシュの要素の数だけブロック内で処理を繰り返して新しい配列を返しています。キーがkey
に代入され、値がvalue
に代入されます。
irb(main):011:0> User.roles_i18n.invert.map{ |key, value| [key, value] }
=> [["一般", "general"], ["管理者", "admin"]]
一見これで良さそうなのですが、実はransackはenumで定義した文字列に対応しておらず、保存される値のvalueを0
や1
などのinteger形式で渡す必要があります。
irb(main):013:0> User.roles
=> {"general"=>0, "admin"=>1}
irb(main):016:0> User.roles['general']
=> 0
irb(main):017:0> User.roles['admin']
=> 1
User.roles[value]
とすれば良さそうですね。
irb(main):012:0> User.roles_i18n.invert.map{ |key, value| [key, User.roles[value]] }
=> [["一般", 0], ["管理者", 1]]
[第三引数] { include_blank: t('defaults.unspecified') }
include_blankオプション
は、先頭に表示されるメッセージに空白行を表示するオプションです。
t('defaults.unspecified')
は、ロケールファイルで「指定なし」と定義されています。
今回は指定した文字列が表示されるようになっています。
[第四引数] { class: 'form-control mr-1' }
クラス属性を書いています。
終わりに
f.selectの文法が少し難しかったですね。お疲れさまです。
参考記事

コメント