enum_helpでenumをi18n対応させる&ransackのプルダウン検索に対応させる
前提
AdminLTEを使って管理者用機能を実装する(トップページ) - Ruby on Rails Learning Diary
ransackで検索機能を実装する - Ruby on Rails Learning Diary
enum role: { general: 0, admin: 1 }
Userモデルのrole
カラムで一般ユーザーと管理ユーザーを管理している。
実装したいこと
- enumで定義したものを翻訳して表示させたい
- ユーザー一覧画面で、翻訳した状態でプルダウン検索できるようにしたい
実装の流れ
- EnumHelp導入
- 翻訳ファイルの設定
- Controllerの設定
- Viewの設定
Install EnumHelp
EnumHelpとは、Enumで定義した値を簡単に翻訳できるgemです。
公式に沿って導入していきます。
GitHub - zmbacker/enum_help: Help ActiveRecord::Enum feature to work fine with I18n and simple_form.
gem 'enum_help'
ターミナルでbundle install
します。
I18n local file
ja: enums: user: role: general: '一般' admin: '管理者'
enumsと複数系になります。
私はenumと記述してエラーを発生させてしまいました。
View
Viewではこのように呼び出すことができます。
<%= user.role_i18n %>
ransackのプルダウン検索実装
ここまでは簡単だったのですが、プルダウン形式で検索対応させるのに苦戦して時間がかかってしまいました。
Controller
def index @q = User.ransack(params[:q]) @users = @q.result(distinct: true).order(created_at: :desc).page(params[:page]) end
View
<%= search_form_for @q, url: admin_users_path do |f| %> #送信先はadmin_users_path <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: '検索ワード' %> </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 t('defaults.search'), class: 'btn btn-primary col-auto' %> </div> </div> </div> <% end %>
f.select
そもそも、f.select
が分かっていなかったです。
<%= f.select :保存されるカラム名, [ ["表示される文字","保存される値"], ["表示される文字","保存される値"], {オプション}, {htmlオプション} ] %> #class: "form-control"とか。
第三引数のオプションを定義する部分と第四引数のhtmlオプションは省略できます。 ただし、第三引数のオプションを定義しないで第四引数のhtmlオプションを定義するときは第三引数は省略できないので空の{ }として記述します。
第三引数にはpromptオプションやinclude_blankオプションを指定できます。 promptオプションは未選択の時に一番上に表示されるメッセージを定義できるオプションです。 include_blankオプションは先頭に表示されるメッセージに空白行を表示するオプションです。 include_blank: trueのような形で使用します。 使用しないときはfalseを定義します。 またtrue、falseの部分に文字列を入れるとその文字が表示されます。
【開発メモ】Ruby on Railsのform_forでドロップダウンリストの選択ボックスを設置する方法 | FREE SWORDER
難しかったところは、ここです。
<%= f.select :role_eq, User.roles_i18n.invert.map{|key, value| [key, User.roles[value]]}, { include_blank: '指定なし' }, #第3引数。先頭に表示するメッセージ { class: 'form-control mr-1' } %> #第4引数。htmlオプション。
使用しているメソッドは…
eq
(equals) ransackのメソッド。等しい値を持つレコードを返す。
invert
メソッドはkeyとvalueを入れ替えてくれる。このメソッド知らなかった。
map
メソッドは、ブロックの戻り値を新しい配列の要素にして返してくれる。
invert (Hash) - APIdock Basic Searching · activerecord-hackery/ransack Wiki · GitHub
EnumHelpを導入したことにより、User.roles_i18n
というクラスメソッドで、
{キー1 => 値1, キー2 => 値…}
というハッシュを取得できる。
f.selectに合わせて、["表示される文字","保存される値"],
の形にしなければらならない。今のままだと逆になっている。
そこで、invert
メソッドでキーと値を入れ替えてあげる。
そして、map
メソッドを使って、取得したハッシュを新しく配列にして返す。
配列.map { |変数| 実行する処理 }
mapメソッドは、配列だけではなくHashに対しても使用することができます。 キーがkeyに代入され、値がvalueに代入されます。 ただし、返り値はハッシュではなく、配列になるので注意してください。
【Ruby】mapメソッドの基礎から応用をマスターして、効率的なコード | Pikawaka - ピカ1わかりやすいプログラミング用語サイト
単純にこれ [key, value]
だと、valueがgeneralやadminとなっている。
ransackではenumで定義したstringには対応しないので、integerにして渡してあげる必要がある。
だから、[key, User.roles[value]]
こうしてvalueをintegerにする。
RansackはRailsのenumに対応していないっぽい | 地方でリモートワーク
今回苦戦したのは、Rubyの文法が分かっていなかったからなので、チェリー本を早く終わらせます。
また、すぐ個人ブログを検索してコードを真似して実装しちゃいがちなのですが、公式だけで実装できるようになりたいです…
エラー
ユーザー編集ページで、権限を一般ユーザーと管理者で選択できるようにしたくて、このようにしたら1 is not valid role
とエラー。
<%= f.select :role,[['general',0], ['admin', 1]], {}, class: 'form-control' %>
これで登録できない??最初は数字をstringで読み込んでいるから??とか思ったけど、enumに合わせる必要があるみたいです。(何故かはよく分かってない) enum に登録した定数でないと、モデルに登録できない。さっきのransackとは逆でややこしいです。
<%= f.select :role, {一般: 'general', 管理者: 'admin'}, {}, class: 'form-control ' %> #これでもできるけど、 <%= f.select :role, User.roles_i18n.invert, {}, class: 'form-control' %> #こっちのがスマート
参考
Action View フォームヘルパー - Railsガイド
【Rails】enumでのセレクトボックス 作成~都道府県など~ - 未経験からのフルスタックエンジニア
検索用のgem"Ransack"の簡易チュートリアルが書き上がってしまったので公開する - Qiita