検索結果の絞りこみ かなり拙い理解 ransackその1(その2あり)

 

要点が絞れているか、理解しているのかよくわからないので追記・修正予定で書く

最初に複数条件での検索が出来た時のformは

= form_tag(detailsearch_path,:method => 'get') do
= search_form_for @search do |f|
.form-group
= f.label :name_cont, "キーワード検索"
= f.text_field :name_cont, class: "form-control"
.form-group
= f.label :parentcategory_eq, '親カテゴリ'
- @allparentcategories.each do |parentcategory|
= parentcategory.name
= f.radio_button :parentcategory_eq, parentcategory.name

.actions= f.submit "Search"

こんな感じ。インデントは無茶苦茶なので参考にしてはいけない。

ポイントと思われる箇所は

=  search_form_for @search do |f|の中にフォームを書くこと

それと:name_contや:parentcategory_eqの部分。

contは含む物、eqはイコール

 

フォームよりもコントローラーにどんな感じで飛んでるかの方が大事だろう

def search
@items = Item.firstsearch(params[:search])
 
@allparentcategories = Category.where(ancestry: nil)
@search = Item.ransack(params[:q]) #追加
@result = @search.result #追加
end

def detailsearch
@search = Item.ransack(params[:q]) #追加
@result = @search.result #追加
binding.pry
end

本当は検索結果のアクション1つでいいんだろうけど、検索結果のページ作った後に、詳細絞り込みの検索のページを作ったからコントローラー二つになってる。注意点としては検索フォームがある側と結果側の両方に似たようなインスタンス渡さないといけないということ。

 

よくあるパターンとしては検索したらそのページの一部をrenderで書き換える手法だろうから、スマートなパターンとしてhomeの一部にrender @resultするって感じかな

 

今回は詳細検索に渡す用のインスタンスをsearchに渡して,検索するためにもビューで使うためにもdetailsearchでも同じような記述してる。ちなみにdetailsearch.html.hamlでは

- if @result.any?
= render @result

 これだけ。

何故かこれだけで指定してたモデルのindex的な物が表示されてるが、該当するモデルのindexページで条件に合致したものだけ表示する・・・みたいなことしてるのか??

まぁ確かに値入れるだけならransackメソッドで@searchに入れてるから、resultメソッドに何か整形の働きがあると考えるのが自然だろう。後でresultを調べるか・・

 

 それはともかくやはりコントローラーにどんな感じで飛んでくるかが大事だろうからその辺りをもう少し補足。参考記事の中から少し抜粋する。

抜粋前に補足説明しておくとransackのsearchは今回使ってない。別の参考記事にもあるようにransackのsearchは他と競合し易いから使わずにransackメソッドを使うべきだという物があったからである。上の記述例でわざわざfirstsearchにしてるのもどこかで競合したらいやだな、という意図から。

searchの書き方

searchメソッドは一種のDSLになっています。
属性をpredicate(述語)で繋いだ文字列のシンボルをキーに、検索対象を値にして書いていきます。
前述の例だと「name属性に'ほげ'という文字列を含む(countain)」対象を検索します。以下のようなSQLが発行されます。

Item.search(:name_cont => "ほげ").result.to_sql

結果

# => "SELECT `items`.* FROM `items` WHERE `items`.`name` LIKE '%ほげ%')"

 

この例が一番わかり易い。さらにbinding.pryで自分が書いた物の結果を見ると

 

    11: def detailsearch

    12:   @items = Item.firstsearch(params[:search])

    13:   

    14:   @search = Item.ransack(params[:q])  #追加

    15:   @result = @search.result           #追加

=> 16:   binding.pry

    17: end

 

[1] pry(#<HomeController>)> @search

=> Ransack::Search<class: Item, base: Grouping <conditions: [Condition <attributes: ["name"], predicate: cont, values: ["赤ちゃん"]>, Condition <attributes: ["parentcategory"], predicate: eq, values: ["おもちゃ・ホビー・グッズ"]>], combinator: and>>

[2] pry(#<HomeController>)>

こんな感じで

Itemモデルから:nameは赤ちゃんを含む(predicate:cont)物で、かつ

parentcategoryが指定と等しい(predicate:eq)な物が@searchに入ってるのがわかる。

 

コントローラーではparams[:q]でランサックから送られてきたやつ受け取ってんだろうけど、カラムを指定したりも出来るっポイ。というか本来ならprivateでストロングパラメタ記述して、許可したカラムだけransackする感じかも(参考記事にそうしてる物があった)

 

参考記事

ransack

https://qiita.com/nysalor/items/9a95d91f2b97a08b96b0

 

https://qiita.com/nishina555/items/2c1f8bae980e426519bc#%E6%A4%9C%E7%B4%A2%E3%83%95%E3%82%A9%E3%83%BC%E3%83%A0%E3%81%AE%E4%BD%9C%E6%88%90

 

https://qiita.com/nico2525_ki_ra/items/26703918d95e1f3e817d

その他直接使ってはいないもの

https://qiita.com/tomaaaaaaaaa/items/6a7de506103734e8f5ce

https://qiita.com/tomaaaaaaaaa/items/4c2beeb504da058408a4

https://kurose.me/parameter-order-chage/