Help us understand the problem. What is going on with this article?

[Rails] form_with内でのcollection_selectの使い方(ドロップボックスでの検索)

More than 1 year has passed since last update.

簡単にできるかなと思って少しだけハマったのでform_with内でのcollection_selectの使い方を整理します。

実現したい事

  1. ドロップボックスで選択した項目による検索
  2. ドロップボックスの中身はDBから取得

環境

Ruby version 2.5.1
Rails version 5.1.6

サンプル

Userが所属している会社の住所の都道府県で検索したい。
リレーションは下記の様になります。
User(tournament): belongs_to :company
Company(field): has_many :users

また、companyテーブルはprefectureというカラムを持っているとします。

index.html.slim
= form_with(url: search_users_path, method: :get, local: true) do |form|
  = form.label :"都道府県"
  = form.collection_select :prefecture, Company.group(:prefecture).order(id: :desc), :prefecture, :prefecture, {prompt: ""}
  = form.submit

各コードの説明します。

form_with: form_forとform_tagを合わせたものです。詳しくは他の記事を参照してください(【Rails 5】(新) form_with と (旧) form_tag, form_for の違い)。ここではsearch_users_pathにフォームの内容を送る様になっています。
local: trueはform_withはデフォルトで非同期通信で送信する様になっているので、これで無効にしています。(もちろん必要あれば有効にしてください。)

collection_selectはDBの情報を元にドロップボックスを生成するメソッドです。
下記にどの様に使われるかまとめます。コメント部分は上記の例で実際に記述した項目を書いています。

sample.rb
collection_select(
  object, #今回はform_withで既にオブジェクトを渡しているので省略(省略しないとエラーになります。)
  method, #:prefecture : paramsで送る時のkeyになります。つまり今回はparams[:prefecture]で選択したドロップボックスの項目が取得できます。
  collection, #Company.group(:prefecture).order(id: :desc) : 実際にドロップボックスに入る配列を渡します。
  value_method, #:prefecture : optionタグにセットされるvalue属性になります。<option value="ここに値が入る(今回はcompanyの都道府県の値)">
  text_method, #:prefecture : optionタグにセットされる実際のテキストになります。つまり今回の場合は<option value="千葉県">千葉県</option>みたいなhtmlが出力されます。
  options = {}, #{prompt: ""} : オプションです。このオプションではドロップボックスの一番上を空欄に指定しています。
  html_options = {} #省略 : htmlオプションです。
) 

collectionのところでCompany.group(:prefecture).order(id: :desc)としていますが、これはgroupメソッドを使ってCompanyモデルのprefectureカラムから重複を除いたものを出力しています。これをしないで例えばCompany.allとしてしまうと東京の会社が3社あったらドロップボックス内に"東京"が3つ出てきてしまいます。なのでgroupを使ってprefectureが同じものは1つだけ抽出しています。

あとはコントローラで選択した都道府県にある会社を抽出します。

users_controller.rb
#他のアクションは省略
def index
  @users = User.all
end

def search
  companies = Company.where(prefecture: params[:prefecture]) #選択した都道府県にある会社を全て取り出している。
  @users = User.where(company_id: companies.pluck(:id)) #抽出した会社からidだけを取り出して、その会社に所属しているuserを抽出している。
  render "index"
end

pluckの使い方は他の記事やサイト様にお任せしますが、companiesからidだけを抜き出して配列で出力してくれます。

まとめ

見返すと大した事はしていないのですが、結構時間かかったのでまとめました。
もっと良い方法があれば教えていただけると助かります。

参考情報

Can someone explain collection_select to me in clear, simple terms? (Stack Overflow)

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away