[Rails] enum_helpよる多言語対応とransackを使ってプルダウン検索機能を実装

学習記録

はじめに

管理者画面のユーザ一覧画面で、gem ransackenum_helpを使ってプルダウン検索機能を実装していく方法を見ていきます。

なお、今回はransacki18nは既に導入済みとします。詳しいインストール方法は別の記事で紹介しているため、割愛させていただきます。

[Rails] ransack掲示板の検索機能を実装
はじめに掲示板の検索機能を実装する仕方を見ていきましょう。ransackというgemを使っていきます。掲示板一覧画面の検索フォームからは、すべての掲示板を検索対象にしましょう。掲示板ブックマーク一覧画面の検索フォ...

作業の流れ

  • 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を01などの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の文法が少し難しかったですね。お疲れさまです。

参考記事

mapメソッドの基礎から応用の使い方まとめ
mapメソッドは、配列の要素の数だけブロック内で処理を繰り返して、新しい配列を返すメソッドのことです。記事では、mapメソッドの分かりづらい処理の流れを1つ1つ丁寧に解説したり、mapメソッドの基礎から応用まで解説しているので幅広く理解することが出来ます。
Hash#invert (Ruby 3.2 リファレンスマニュアル)
値からキーへのハッシュを作成して返します。

コメント

タイトルとURLをコピーしました